Java 为什么EJB是线程安全的而servlet不是?

Java 为什么EJB是线程安全的而servlet不是?,java,multithreading,servlets,thread-safety,ejb,Java,Multithreading,Servlets,Thread Safety,Ejb,据我所知,servlet容器创建有限的servlet实例和每个servlet实例的多个线程,并重用这些线程和实例 因为一个线程有多个实例,所以它们不是“线程安全的”(尽管我知道用线程安全编码它们并不困难) 另一方面,EJB容器不创建EJB线程,而只重用EJB对象(使用池)。由于EJB实例没有多个线程,因此不存在线程安全问题 我的问题是:为什么会有不同的行为?让EJB作为servlet(线程不安全)工作不是一个好主意吗 我确信我遗漏了一些东西,我想了解遗漏的部分。可能是因为它们的设计目标不同 Se

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

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

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

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


我确信我遗漏了一些东西,我想了解遗漏的部分。

可能是因为它们的设计目标不同

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

ejb是一个更加复杂和高级的框架,旨在尽可能简单地实现事务性业务逻辑。它更重,并且有状态组件。这些显然需要线程安全。我想让无状态bean线程安全也是很自然的

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


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

您最好的答案是直接从接口的Javadoc中得出:

已弃用。自Java Servlet API 2.4起,无需直接替换

公共接口SingleThreadModel

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

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

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


对于您的问题,最简短的回答当然是,让EJB像servlet一样工作是一个好主意,在EJB3.1中,我们添加了一个组件,可以完全做到这一点:
@Singleton

@Singleton
bean可以像servlet一样是多线程的,方法如下:

  • 使用
    @concurrentymanagement(BEAN)
  • 在需要并发性的方法上使用
    @ConcurrencyManagement(CONTAINER)
    以及
    @Lock(READ)
    ,在非线程安全的方法上使用
    @Lock(WRITE)
Servlet多年来拥有的另一个东西是EJB从未拥有的
,它允许Servlet在应用程序启动时急切地加载和工作

为了匹配Servlet
,我们添加了
@Startup
注释,该注释可以添加到任何
@Singleton
EJB中,并将在应用程序启动时使其启动。这些bean将在应用程序启动时调用其
@PostConstruct
方法,并在应用程序关闭时调用其
@PreDestroy

您可以使用
@DependsOn
注释bean,并指定需要在注释bean之前启动的bean列表,而不是使用数字(
1
)来指定使用
@Startup
注释bean的顺序

我们在EJB3.1中为使Servlet和EJB保持一致而做的一个远不为人所知和理解的方面当然是允许EJB被打包在
.war
文件中——这不是不为人所知的部分——当我们这样做时,我们悄悄地改变了
java:comp/env
的定义,以匹配Servlet方法

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


很好的一点啤酒时间,可以用来测试你的朋友。

servlet和ejb都可以开始/提交事务,而且从JavaEE6开始,它们都可以是多线程的。这实际上是EJB专家组比Servlet专家组在相同的决策上更加保守的问题——直到最近几年。在EJB3.2中,我们完全摆脱了“不能使用文件”的限制,这种限制永远不应该存在