我想保证自己的Servlet
之一中的线程安全。 SingleThreadModel
已过时,不保证线程安全。相反,我可以在synchronized
内使用Servlet
块。
您是在Servlet中保证线程安全的另一种方法吗?
如果是,请说明如何实现。
如果不是,我将如何设计以保证Servlet
中的线程安全?
答案 0 :(得分:1)
关于Java Servlet,没有什么特别之处,它或多或少容易受到线程问题的影响,但是由于Web服务器的多线程性质,您在Java Servlet中可能遇到的任何线程问题很快就会变得显而易见。 >一般。
我之所以这么说是因为我想强调 Java Servlets不特殊的观点。您可以轻松编写任何不是线程安全的代码。在Java Servlet中摆脱它要困难得多。
构建应用程序的一种流行技术是(逻辑上和物理上)将代码分为三堆: m odel(您的数据), v iew(用户界面) )和 c 监控程序(通常缩写为“ MVC”)。当您这样做时,您会认为“视图”和“控制器”是无状态。也就是说,模型包含程序/用户/事务/所有内容的所有状态,并且控制器和视图仅根据需要修改该状态并允许该模型在代码中流通。
在Servlet容器中,Servlet本身就是控制器(或视图),并且应该是无状态的。这很容易做到:根本不使用任何包含特定于用户或事务的类的类成员。任何Servlet类成员本身都必须是线程安全的(如ConcurrentMap
)或以线程安全的方式使用(例如,synchronized
阻止对HashMap
的访问)。请记住,即使是线程安全的集合(例如ConcurrentMap
)也可以以非线程安全的方式使用,如下所示:
ConcurrentMap cm = ...;
if(!cm.containsKey("foo")) {
cm.put("foo", createFoo());
}
上面的代码无法阻止两个线程调用createFoo
,这可能是昂贵的(或破坏性的!)操作。您需要使用同步块来避免两个线程之间出现race condition。
那么,您将模型放在哪里?好吧,为此您有几种选择。最简单的方法之一是将所有内容保留在用户的session
中。会话是特定于用户的结构,并且可以通过所有HttpServletRequest
对象使用,因此servlet始终可用。不过,这仅在用户实际登录到您的应用程序时有效。现在让我们假设您确实有用户,并且他们有自己的会话。
会话根据发出请求的用户的身份自动与请求关联。您需要的任何其他数据都应来自请求本身(可能来自query string的请求参数)。
您可以将任何所需的内容放入会话中。描述您的“业务对象”(用于表示特定应用程序中的概念的东西)的自定义类比包含其他Map
,Map
s个命名集合的Lists
对象更可取,等等。我已经看到完整的Web应用程序,除了标准Java库类之外,没有其他任何东西,而且它们杂乱的Map查找和魔术列表索引。不要陷入陷阱:编写自己的类。
用户模型数据的另一种选择是将其存储在某种数据库中。从简单文件存储到关系数据库,再到文档/列/索引存储,“数据库”有许多选项。他们都有自己的优点和缺点。但是它们存储数据,您可以将用户模型数据放入数据库中。您如何识别用户?可能是通过在会话中存储用户ID来实现的。 (因此通常所有这些都返回到会话中)。您还可以使用用户的用户名(可从请求中获得)在数据库中查找用户的数据。
无论如何,您应该不在servlet的类成员中存储有关用户状态的任何信息(包括工作流等)。如果您避免这样做,那么您就永远不必担心servlet中的线程安全。
另一个问题是共享资源的问题。该问题的解决方案是确保以线程安全的方式使用那些共享资源。通常可以使用池化资源来完成此操作,例如与数据库的连接池,消息队列,搜索索引等。解决这些问题通常非常特定于共享的资源类型,因此您可能要问一个更具体的问题当您到达那个阶段。