SingleThreadModel 已被弃用并且不保证线程安全,保证 Servlet 中线程安全的最佳 soluation/design 方法是什么?

SingleThreadModel is deprecated and does not guarantee thread safety, what is the best soluation/design approch to guarantee thread safety in Servlet?

我想在我的 Servlet 之一中保证线程安全。 SingleThreadModel 已弃用,不保证线程安全。相反,我可以在我的 Servlet.

中使用 synchronized

有没有另一种方法来保证 Servlet 中的线程安全?

如果是,请说明如何实现。

如果没有,我在Servlet中保证线程安全的设计方法是什么?

Java Servlet 并没有什么特别之处使它或多或少容易受到线程问题的影响,但是您在 Java Servlet 中可能遇到的任何线程问题很快就会变得明显,因为Web 服务器的多线程特性 一般

我说以上只是因为我想强调 Java Servlet 并不特殊 的观点。您可以轻松编写任何非线程安全的代码。在 Java Servlet 中很难摆脱它。

构建应用程序的一种流行技术是(在逻辑上和物理上)将您的代码分成三堆:m模型(您的数据),v iew (UI),以及 controller——通常缩写为 "MVC"。当您这样做时,您认为 "view" 和 "controller" 是 stateless。也就是说,模型包含 program/user/transaction/whatever 的所有状态,控制器和视图只需根据需要修改该状态并允许该模型在代码中流动。

在servlet 容器中,Servlet 本身要么是控制器(要么是视图),应该是无状态的。这 非常 容易做到:只要不使用任何 class 包含任何特定于用户或交易的成员即可。任何 Servlet class 成员本身必须是线程安全的(如 ConcurrentMap)或以线程安全的方式使用(例如 synchronized 阻止访问 HashMap)。请记住,即使是线程安全的集合(例如 ConcurrentMap)也可以以非线程安全的方式使用,如下所示:

ConcurrentMap cm = ...;
if(!cm.containsKey("foo")) {
    cm.put("foo", createFoo());
}

上面的代码不能阻止两个线程调用 createFoo,这可能是一个昂贵的(或破坏性的!)操作。您需要使用同步块来避免两个线程之间的 race condition

那么,你把模型放在哪里?好吧,您有多种选择。最简单的方法之一是将所有内容保存在用户的 session 中。会话是一种特定于用户的结构,可用于所有 HttpServletRequest 对象,因此它始终可用于 servlet。不过,这仅在用户实际登录到您的应用程序时才有效。现在让我们假设您确实有用户并且他们有自己的会话。

会话会根据发出请求的用户的身份自动与请求相关联。您需要的任何其他数据都应该来自请求本身(可能是来自 query string 的请求 参数 )。

您可以将任何您想要的内容放入会话中。描述您的 "business objects" 的自定义 classes(您在特定应用程序中用来表示概念的东西)优于包含其他 Maps 命名集合的 Map 对象, Listss 等。我见过完全使用标准 Java 库 classes 构建的 Web 应用程序,它们是一堆地图查找和魔法列表索引。不要落入那个陷阱:编写自己的 classes.

用户模型数据的另一种选择是将其存储在某种数据库中。 "database" 有很多选择,从简单的文件存储到关系数据库再到 document/column/index 存储。他们都有自己的长处和短处。但是它们存储数据,您可以将用户模型数据放入数据库中。你如何识别用户?可能通过在会话中存储用户 ID。 (所以它通常都回到会话)。您还可以使用用户的用户名(可从请求中获得)在数据库中查找用户的数据。

无论如何,您应该在 servlet 的 class 成员中存储任何关于用户状态(包括工作流等)的信息。如果你避免这样做,你就不会担心 servlet 中的线程安全。

另一个问题是共享资源。该问题的解决方案是确保以线程安全的方式使用这些共享资源。这通常可以通过池化资源来完成,例如数据库的连接池、消息队列、搜索索引等。解决这些问题通常非常特定于共享资源的类型,因此您可能想问一个更具体的问题当你到达那个阶段时。