9

根据我的理解,servlet 容器创建有限的 servlet 实例和每个 servlet 实例的多个线程,并重用这些线程和实例。

因为一个线程有多个实例,所以它们不是“线程安全的”(尽管我知道用线程安全对它们进行编码并不困难)。

另一方面,EJB 容器不创建 EJB 线程,而是仅重用 EJB 对象(使用池)。由于 EJB 实例没有多个线程,因此不存在线程安全问题。

我的问题:为什么会有不同的行为?让 EJB 作为 Servlet 工作(线程不安全)不是一个好主意吗?

我确定我遗漏了一些东西,并想了解那个遗漏的部分。

4

3 回答 3

9

您的问题的最短答案当然是让 EJB 能够像 Servlet 一样工作是一个好主意,在 EJB 3.1 中,我们添加了一个可以完全做到这一点的组件:@Singleton

一个@Singletonbean 可以像 servlet 一样是多线程的,通过以下任一方式:

  • 使用@ConcurrencyManagement(BEAN)
  • 与需要并发的方法和非线程安全的方法@ConcurrencyManagement(CONTAINER)一起使用。@Lock(READ)@Lock(WRITE)

Servlet 多年来一直拥有而 EJB 从未拥有的另一件事是,<load-on-startup>它允许 Servlet 急切地加载并在应用程序启动时工作。

为了匹配 Servlet <load-on-start>,我们添加了@Startup可以添加到任何@SingletonEJB 的注解,并将导致它在应用程序启动时启动。这些 bean 将@PostConstruct在应用程序启动时调用它们的方法,并@PreDestroy在应用程序关闭时调用它们。

与其使用数字 ( ) 来指示使用start 注释的 bean<load-on-startup>1</load-on-startup>的顺序,不如使用并指定需要在带注释的 bean 之前启动的 bean 的列表。@Startup@DependsOn

我们在 EJB 3.1 中为对齐 Servlet 和 EJB 所做的一个鲜为人知和理解的方面当然是允许将 EJB 打包到.war文件中——这不是鲜为人知的部分——当我们这样做时,我们悄悄地改变了定义ofjava:comp/env以匹配 Servlet 方法。

在 EJB 3.1 之前,不可能有两个 EJB 共享一个java:comp/env名称空间(java:comp/env在 EJB 规范中是 bean 范围的)。相比之下,Servlet 从来没有任何方式让单个 Servlet 拥有自己的私有java:comp/env命名空间(java:comp/env在 Servlet 规范中是模块范围的)。因此,在 EJB 3.1 中,打包在 war 中的 EJB 将具有与 webapp 中的所有其他 Servlet 和 EJB 相同的模块范围命名空间,这与打包在 EAR 中时 EJB 获得java:comp/env的 bean 范围命名空间形成了很大的对比java:comp/env一场战争之外。我们就那个争论了好几个星期。

不错的一点啤酒时间琐事,可以用来测验你的朋友。

于 2012-06-27T01:30:23.767 回答
9

可能是因为它们在设计时没有考虑到相同的目标。

servlet API 是一个简单的 API,非常接近 HTTP 协议,您可以在其之上构建应用程序或框架。HTTP 协议是完全无状态的,我想构建一个无状态的 API 也是有意义的。几个建立在 servlet API 之上的框架(例如 Stripes),每个请求使用一个 Action 实例,它不是同时使用的。

EJB 是一个复杂得多的高级框架,旨在尽可能简单地实现事务性业务逻辑。它更重量级,并且具有有状态的组件。这些显然需要是线程安全的。我想因此使无状态 bean 成为线程安全的也是很自然的。

应该注意的是,例如 Spring bean,默认情况下是单例的,因此必须遵循与 servlet 相同的规则。因此,多种设计可以提供或多或少相同的功能。

线程与性能优化无关。如果需要同时处理 3 个请求,则需要 3 个线程,无论请求是发送到 servlet 还是发送到 EJB。

于 2012-06-26T22:13:44.217 回答
3

您的最佳答案直接来自javax.servlet.SingleThreadedModel接口的 Javadoc:

已弃用。从 Java Servlet API 2.4 开始,没有直接替换。

public interface SingleThreadModel

确保 servlet 一次只处理一个请求。这个接口没有方法。

如果一个 servlet 实现了这个接口,就可以保证在 servlet 的 service 方法中不会有两个线程同时执行。servlet 容器可以通过同步对 servlet 的单个实例的访问,或者通过维护一个 servlet 实例池并将每个新请求分派给一个空闲的 servlet 来保证这一点。

请注意,SingleThreadModel并不能解决所有线程安全问题。例如,会话属性和静态变量仍然可以被多个线程上的多个请求同时访问,即使使用 SingleThreadModel servlet 也是如此。建议开发人员采取其他方式来解决这些问题,而不是实现此接口,例如避免使用实例变量或同步访问这些资源的代码块。此接口在 Servlet API 版本 2.4 中已弃用。

于 2012-06-26T22:39:25.740 回答