为什么EJB线程安全且servlet不安全?

时间:2012-06-26 22:01:10

标签: java multithreading servlets thread-safety ejb

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

因为线程有多个实例,所以它们不是“线程安全的”(虽然我知道使用Thread-safety编写它们并不困难)。

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

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

我确定我错过了一些东西,并希望了解那个缺失的部分。

3 个答案:

答案 0 :(得分:8)

可能是因为他们的设计并没有考虑到相同的目标。

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

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

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

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

答案 1 :(得分:8)

对你的问题最简单的回答当然是让一个EJB像Servlet一样工作是个好主意,在EJB 3.1中我们添加了一个可以做到这一点的组件:@Singleton

@Singleton bean可以像servlet一样多线程,通过:

  • 使用@ConcurrencyManagement(BEAN)
  • 对需要并发的方法使用@ConcurrencyManagement(CONTAINER)@Lock(READ),对非线程安全的方法使用@Lock(WRITE)

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

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

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

我们在EJB 3.1中为了对齐Servlet和EJB而做的一个鲜为人知且理解的方面当然是允许将EJB打包到.war文件中 - 这不是鲜为人知的部分 - 当我们这样做了,我们悄悄地改变了java: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相同的模块范围java:comp/env命名空间,这与bean范围的{{1}形成鲜明对比。 EJB在战争之外的EAR中打包时得到的命名空间。我们对这个问题进行了数周的辩论。

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

答案 2 :(得分:3)

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

  

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

     

public interface SingleThreadModel

     

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

     

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

     

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