2012-06-26 121 views
7

根据我的理解,一个servlet容器为每个servlet实例创建有限的servlet实例和多个线程,并重用这些线程和实例。为什么EJB线程安全并且servlet不是?

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

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

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

我确定我错过了一些东西,并且想要了解那些缺失的部分。

+0

他们为什么要一致?他们做了完全不同的事情。 – EJP

+0

@EJP,对,他们确实解决了不同的目的,但他们在无状态方面有相似之处。有了这种相似性,EJB可以通过拥有线程而不是仅具有实例来提高性能。 –

+0

如果你对一个servlet /一个实例感兴趣 - 你看看这篇文章:http://piotrnowicki.com/2012/04/one-servlet-instance-to-rule-them-all/ –

回答

8

可能是因为他们没有设计出相同的目标。

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

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

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

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

+2

Servlet和EJB都可以开始/提交事务,并且从Java EE 6开始,它们都可以是多线程的。这确实是EJB专家组比Servlet专家组更加保守的决定 - 直到最近几年。在EJB 3.2中,我们完全摆脱了那些不应该超过推荐的虚假“不能使用文件”的限制,应该已经在EE规范而不是EJB规范中。 –

+0

谢谢JB的详细解释。据我所知,与EJB相比,Servlet通常是轻量级的。但另一方面,无状态EJB(它是最常见的EJB类型)提供了类似的功能,并且不会存储任何状态。与servlet类似,EJB可以很容易地以“线程安全”的方式编写。因为这是可行的,创建线程比创建实例更有效。这不正确吗? –

+2

首先,无状态bean可以有状态。他们不可能有会话状态,这是非常不同的。其次,正如David Blevins在他的回答中所解释的那样,您可以使用与servlet类似的单例。第三,在EJB和servlet中使用线程来处理并发请求。唯一的区别是所有并发线程都使用单个servlet实例,而EJB一次只能由一个线程使用。为什么让你认为创建一个bean实例是如此昂贵?例如,使用Java创建对象比执行SQL查询要快很多个数量级。 –

3

你最好的答案是直出的Javadoc为javax.servlet.SingleThreadedModel接口:

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

public interface SingleThreadModel

确保小服务程序处理在一个时间只有一个请求。这个接口没有方法。

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

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

8

最简短的回答你的问题,当然这是一个好主意,有可能使EJB来如servlet和EJB 3.1的工作,我们补充说,能做到这些组件:@Singleton

@Singleton豆可以多线程等小服务程序,或者:

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

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

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

除了使用数量(<load-on-startup>1</load-on-startup>),以决定在与@Startup开始注解的bean,你可以注释豆@DependsOn,并指定需要注解的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中,包装在战争中的EJB与Web应用中的所有其他Servlet和EJB具有相同的模块范围java:comp/env命名空间,这与EJB在打包时获得的bean范围java:comp/env命名空间形成鲜明对比战争之外的EAR。我们几周来就这个问题进行了辩论。

不错的啤酒时间微不足道的测验你的朋友。

+0

这对我来说是好消息,JEE6中的EJB提供多线程选项。这让我很高兴,我的理解是正确的轨道:) –

相关问题