Weblogic线程池中的ThreadLocal安全性

5

我正在编写一个应用程序,使用MDBs、EJBs,并需要使用ThreadLocal在多个EJBs和Helper类之间传递和记录变量,直到事务完成。

流程是:

从MDB的onMessage()开始 -> 一些业务代表EJBs -> 一些helper

问题:

这个应用程序在Weblogic中运行,Weblogic从它的线程池中重新使用线程。所以是否存在跨线程数据损坏的可能性?使用ThreadLocal.remove()的解决方案是否足够安全?

除了将对象作为参数传递给所有方法之外,还有没有替代ThreadLocal的方法?

3个回答

7
WebLogic不会在线程返回池时重置用户设置的ThreadLocal变量 - 用户需要负责管理它们。当这些线程被重用时,它们很可能会相互干扰。由于线程本地引用没有清理,您可能会遇到内存泄漏问题。在将线程返回容器之前,您可以安全地重置线程本地变量。ThreadLocal.remove()调用应该清理它(确保在finally块中完成)。
请注意,如果涉及任何异步或rmi调用,则您的线程本地变量将不会传播。您可能需要考虑WebLogic WorkArea功能,它允许上下文在线程、客户端和服务器之间传播。更多细节可以在http://download.oracle.com/docs/cd/E17904_01/web.1111/e13706/context.htm#i1058690找到。

6
在EJB层无法可靠地使用ThreadLocal。 即使您的代码现在似乎“工作正常”,如果有人远程部署了您的一个bean,会发生什么情况?从 EJB限制 中可以看到:
“为什么禁止线程创建和管理?” EJB规范将线程管理责任分配给EJB容器。允许企业Bean实例创建和管理线程将干扰容器控制其组件生命周期的能力。线程管理不是业务功能,它是一个实现细节,通常非常复杂且平台特定。让容器管理线程可减轻企业Bean开发人员处理线程问题的负担。多线程应用程序仍然可能存在,但对多线程的控制位于容器而不是企业Bean中。
如果您需要共享状态,则应将其作为参数传递到EJB方法中。这种方法行不通吗?另一种选择是将其暂时转储到事务注册数据库或缓存中。

是的,我们的替代方案是将其作为参数传递。但是这个对象需要通过超过15个方法调用,因此它会污染所有我们的接口和实现。我们希望保持它的非侵入性 - 它实际上是一个日志记录对象,并由组织强制执行。 - JoseK
1
我不认为你是独自面对这个问题,它似乎是一个相当普遍的模式。虽然很不幸,但这种做法与 EJB 这样的组件框架的整个目的有些违背。毕竟,你并不是在每个方法调用中传递事务或安全状态,那么为什么要传递日志状态呢?话虽如此,如果你确实走参数路线,我会鼓励你把这些信息放在一个更强大的容器中,比如 CallingContext 对象,并将其传递出去。这样,当你需要添加下一个伟大的功能时,你就不必重构所有的接口了。 - jonathan.cone

1

@JoseK:虽然我没有尝试过你在问题中描述的内容,但是这是我的想法:

  1. 无论是MDB还是Session Bean都是线程安全的。这意味着假设有10个bean池,只有10个请求会同时处理。其他请求将排队等待它们的轮到。因此,一个正在运行的线程本地数据不应该干扰其他线程。

  2. 如果您确信未来也始终使用本地EJB,则我真的不认为在使用线程本地数据时存在任何问题。因为您实际上并没有创建线程。

  3. 虽然Weblogic提供了来自线程池的线程,但该线程专门用于每个请求流,我不认为其本地数据会被破坏。

  4. 正如我所说,我自己没有尝试过,我会尝试以下内容:

在MDB层(您的第一层)中,执行Thread.getCurrentThread.setName(name),并在后续层中打印线程名称,例如Thread.getCurrentThread.getName)

使用不同大小的ejb池、线程池进行多次运行。为每个请求流程分配不同的线程名称。尝试同时运行多个请求。看看是否会出现混合线程名称的情况。

5. 以上所述,为了保持简单并支持未来的远程 EJB,我还会将 CallingContext 接口传递给每个层。


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