EJB 池与线程安全和@PreDestroy

EJB Pooling vs Thread-safe and @PreDestroy

我无法理解 EJB 容器如何使用实例变量管理 @Stateless bean 的线程安全。所以我先举个简单的例子再说明我的顾虑:

@Stateless
public class BeanTest{

@Inject
private String var;

private Connection connection;

@Resource(name = "jdbc/TestDB")
private DataSource dataSource;

public void modify() {
    var = "TestName";
}

@PostConstruct
public void initialize() {
    try {
        connection = dataSource.getConnection();
    } catch (SQLException sqle) {
        sqle.printStackTrace();
    }
}

@PreDestroy
public void cleanup() {
    try {
        connection.close();
        connection = null;
    } catch (SQLException sqle) {
        sqle.printStackTrace();
    }
}
}

这是我的问题,假设我们的容器支持池化:

1.池化与线程安全:

用户 1 使用 BeanTest 的实例并使用修改方法修改 var,然后他完成,容器将 BeanTest 的实例放入托管池中。当用户 2 尝试对请求使用相同的 bean 时,他 可能 获得最初由用户 1 修改的 BeanTest 的相同实例(我知道这也可能获得另一个实例) .那么他会找到实例变量 var 的哪个状态(默认值为 null"TestName")?如果它是新修改的,那是否意味着即使 @Stateless bean 也不是 100% 线程安全的?所以最终没有关于线程安全的容器附加值,因为不使用实例变量使 bean 线程安全,即使它不是 @Stateless bean

2。池化 vs @PreDestroy

如果 bean 返回到托管池并且没有被销毁,这是否意味着 @Predestroy 方法将不会被调用,在这种情况下连接将保持打开状态?所以,如果我们在池中有 30 个实例,我们可能有 30 个未使用的打开连接,这不是性能问题吗?或者这不是 @Predestroy 与池化结合的方式? (使用 Connection 只是一个例子,我们可能还有其他类型的资源需要在 @Predestroy 中关闭)

注意: 这不是现实生活中的例子,所以我不是在寻找替代解决方案,我关心的是理解整个概念和应用服务器中的事物是如何管理的,幕后发生了什么

  1. 如果注入的AnotherBean是无状态的,那么调用AnotherBean.setName()就没有意义了。如果它是有状态的,那么将有状态的 bean 注入无状态的 bean 是没有意义的(检查有状态的 bean 是做什么用的)。关于线程安全:容器无法使您的 类 线程本身安全,但它保证单个客户端线程将一次使用特定的 EJB 实例(检查 Chris 的回答中的引文)。因此,如果您不在 EJB 客户端的不同线程中使用 EJB,那么通过使用 thread confinment.
  2. ,您的 代码是线程安全的
  3. 除了在无状态 bean 中注入实例变量外,不要使用实例变量。否则你与你的 @Stateful 注释不一致。因此,删除 connection 状态变量。无论如何,您不想只为您的 bean 长时间保持打开连接:每次都从数据源获取它。也不要忘记在使用后关闭连接(在 try/finally 子句中)。使用池时,它的工作方式类似于 JDBC 连接池:当 pool/EJB 容器决定它不再需要该实例时(例如,当它想要减少实例数量或当非 application exception ] 被抛出),然后将调用 @PreDestroy 方法。

1) 容器保证线程安全,这意味着单个线程一次可以访问给定的 SLSB。它与保证状态不会改变无关。顺便说一句,在 SLSB 中具有 可变 状态完全没有意义。

2) 您不希望在 SLSB 的生命周期内保持连接,因为您实际上是在防止它冒着耗尽的风险返回池。

我觉得你说的Thread-Safety就是不可重入子句

The Responsibilities of the Container Provider Enterprise JavaBeans 3.2, Final Release Session Bean Component Contract April 10, 2013 2:59 pm Oracle

4.10.13 Non-reentrant Instances The container must ensure that on ly one thread can be executing a st ateless or stateful session bean instance at any time. Therefore, statef ul and stateless session beans do not have to be coded as reentrant. One implication of this rule is that an application ca nnot make loopback calls to a stateless or stateful session bean instance

您可以在无状态会话 bean 中忽略 EJB 编程限制,创建一组线程并执行非线程安全的操作。容器不会阻止你。由于线程管理冲突,这可能会导致各种错误。

容器只承诺一次只允许 1 个线程访问无状态 ejb。 (关于单身人士有一些不同的规则)

你是对的,如果将实例 returned 到池中,你的示例中的连接 可以 建立。因为 been 实例当然仍然存在。

如果您深入研究应用服务器文档,即 Glassfish EJB 池调整章节,您会发现默认情况下是销毁对象实例而不是 return 将它们销毁到池中。与 JDBC 连接相同,它们将被关闭并清理。重用是一种选择,在这种情况下,如果您尝试在 SSB 中创建状态,则可能会消耗一些额外的内存。如果实例在池中闲置,我认为不会对性能产生太大影响。

确切的池实现取决于应用程序服务器供应商,只要他们遵守规范即可。我想你会发现默认行为是在使用后销毁实例。这会导致托管资源被清理。

但这一切都有些无声,在您的示例中,您正试图在 class 中存储一个状态,即连接字段。无需在无状态组件中创建状态,这不是此类组件的用途。 Java EE 架构的其他部分处理状态。 (实体、statefulbeans、JCA)