无状态会话Bean与单例会话Bean

31

根据Java EE 6教程,以下特点可选择无状态会话bean来提高性能:

如果以下情况适用于bean,您可以选择无状态会话bean以提高性能:

  • bean的状态对于特定客户端没有数据。
  • 在单个方法调用中,bean为所有客户端执行通用任务。例如,您可以使用无状态会话bean发送确认在线订单的电子邮件。
  • bean实现了Web服务。

单例会话bean适用于以下情况:

  • 需要跨应用程序共享状态。
  • 需要同时访问单个企业bean的多个线程。
  • 应用程序需要企业bean在应用程序启动和关闭时执行任务。
  • bean实现了Web服务。

但如果:

  • 应用程序不需要共享状态
  • 多个线程可以同时访问单个企业bean
  • 不需要在启动或关闭时执行任何任务

例如,我有一个具有以下接口的登录服务:

public interface LoginService {
  boolean authenticate(String user, String password);
}

应该使用@Singleton还是@Stateless进行注释?它们各自的好处是什么?如果LoginService需要被注入一个EntityManager(可能会被并发使用),该怎么办?

补充:我正在考虑Spring服务bean的Java EE对应项,它们是无状态单例。如果我理解正确,Java EE的对应项是@Stateless session bean和@Singleton bean,用于在启动时配置应用程序、清理关闭或保存应用程序范围内的对象。这个理解正确吗?

8个回答

12

我建议使用无状态(Stateless) - 服务器可以生成多个 bean 实例并并行处理传入的请求。

单例(Singleton)听起来可能会成为瓶颈 - 默认的 @Lock 值是 @Lock(WRITE),但可以更改为针对 bean 或单个方法的 @Lock(READ)。


4
为什么单例模式会被视为瓶颈,如果我们可以对整个类使用@Lock(LockType.READ)呢?例如:@Lock(LockType.READ)public class MySingleton {...}。 - user2022068
2
我在想这是否是Stateless的一个缺点,mjn。使用Stateless,我们需要一个大小为N的池来处理N个并发请求吗?假设使用@Lock(READ)的Singleton可以在不调整池大小的情况下处理N个并发请求。 - DavidS

4
根据EJB 3.1规范第4.8.5章节第110页“Singleton并发性”所述:允许在Singleton bean实例状态中存储不支持并发访问的Java EE对象(例如Entity Managers、Stateful Session Bean引用)。然而,Bean开发人员有责任确保这些对象不被多个线程同时访问。
此外,根据hibernate entitymanager documentation所述:EntityManager是一种廉价、非线程安全的对象,应该仅在单个业务流程、单个工作单元中使用一次,然后丢弃。
对我而言,这意味着您不应该将EntityManager注入单例EJB中。如果我需要在此类中实现的所有内容都支持并发且无需进行额外的锁定/同步,则会将单例EJB用作无状态EJB的替代品。由于您或其他程序员可能会忽略此问题,因此我个人更喜欢除了启动相关问题或可以作为独立单元(独立于其他bean)实现的功能之外,不使用单例EJB。从这个意义上说,似乎不建议将无状态EJB注入到单例中。这样做引发了一个问题,即容器实际上何时执行将SLSB注入到Singleton中的操作?根据EJB 3.1规范第4.8章,依赖注入在单例bean实例可被客户端访问之前完成。因此,单例显然会坚持使用相同的SLSB实例,这似乎会隐式地成为单例,但似乎没有任何保证。至少我在规范中找不到任何东西,因此行为可能是不可预测的,或者在最好的情况下是特定于容器的,这不是大多数人想要的。
因此,我只会将Singleton注入到Singleton或Singleton注入到SLSB中,反之不行。对于将Singleton注入到Singleton的情况,规范为您提供了定义单例之间依赖关系的机会,以便容器可以按正确的顺序初始化它们(请参见ejb 3.1规范第4.8.1章有关@DependsOn注释的内容)。

2

@Stateless 允许你在JVM中准备多个副本进行处理(只要内存和池大小允许),而 @Singleton 在JVM中只有一个副本,即使单个副本可以支持多个并发线程运行。

在性能方面,@Singleton 更好,前提是它使用的资源允许长时间运行访问。然而,在分布式环境下,有时会发生一些不好的事情,例如数据库或网络连接可能会失败。

使用 @Stateless bean,访问时间更短暂。此外,如果出现故障,它将重新启动并尝试与资源建立新连接。如果在单例模式下发生这样的情况,则单例模式必须处理它,而不需要应用程序重新启动,因为 @PostConstruct 仅在每个JVM调用一次。

对于大多数情况,我更喜欢一点容错性而非性能,特别是在我无法控制的系统上。


1

我认为在并发使用中,Singleton 不会比 SLSB Pool 更差,甚至可能更好。唯一的问题是如果你想在线程之间共享某些东西,你需要锁定它,这可能会对性能造成很大的问题。所以在这种情况下,SLSB Pool 的表现要好得多,因为它不是 100% 的 Singleton,有更多的实例,一个被锁定,另一个就会出现。但是,如果锁定的是所有 SLSB 共享的某些资源,池也无法帮助。

简而言之,我认为 Singleton 比 SLSB Pool 更好,如果可以的话应该使用它。它也是 Spring Beans 的默认范围。

我不是 JavaEE 专家,这只是我的感觉,请纠正我如果我错了。


0

依我之见,我的回答是:

“没有应用程序需要共享的状态”导致选择无状态会话Bean,因为句子“为了提高性能,您可能会选择无状态会话Bean......”。

考虑到“单个企业Bean可以被多个线程同时访问”,您将不得不使用单例。如果我理解正确,当正确使用时,甚至不可能同时访问无状态bean。

“启动或关闭时没有任务需要执行”的话对我来说并不重要。如果必须执行任务来正确设置Bean,则必须通过@PostActivate方法调用它们。

我会从逻辑上得出您的问题是要使用@Singleton,因为您询问了并发访问。当然,您将不得不手动控制对任何其他资源(不是EJB)的访问同步。


0

我认为你应该使用单例会话 bean。因为登录服务应该是全局服务,并且不需要为具体用户或调用存储任何状态。


0
如果您有任何资源将在整个应用程序中保持不变,例如从某个文件加载数据或引用数据,那么您应该选择Singleton。否则,请选择SLSB。 SLSB 的缺点是会创建多个对象,因此会占用更多的内存。

0

如果您确定没有在线程之间共享状态,那么单例模式就可以了。在这种情况下,您还应该使用@ConcurrencyManagement( ConcurrencyManagementType.BEAN )注释类,以允许多个线程同时运行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接