Jboss Java EE 容器和一个 ExecutorService
Posted
技术标签:
【中文标题】Jboss Java EE 容器和一个 ExecutorService【英文标题】:Jboss Java EE container and an ExecutorService 【发布时间】:2012-12-05 14:33:51 【问题描述】:我有一个独立的 java 应用程序,它使用 ExecutorService 来并行处理多个作业
ExecutorService es = Executors.newFixedThreadPool(10);
我现在想在 EJB bean 中重复使用相同的解决方案,但不确定如何正确初始化 ThreadPool,因为我通常会离开 Java EE 容器来控制所有线程资源。我可以只使用相同的代码还是有其他正确的方法来获取 Jboss 托管线程池?
【问题讨论】:
【参考方案1】:强制性警告:不鼓励在 Java EE 应用服务器(甚至 Tomcat)中创建自己的线程,因为这可能是一个巨大的性能问题,并且在大多数情况下会阻止容器功能(例如 JNDI)工作。新线程不知道自己属于哪个应用程序,不会设置线程上下文类加载器以及许多其他隐藏问题。
幸运的是,有一种方法可以让 Java EE 服务器通过 Java EE 6 @Asynchronous
和这种巧妙的设计模式来管理线程池。可移植到任何经过 Java EE 6 认证的服务器。
在您的应用程序中创建此 EJB。
package org.superbiz;
import javax.ejb.Asynchronous;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
@Stateless(name="Executor")
public class ExecutorBean implements Executor
@Asynchronous
@Override
public void execute(Runnable command)
command.run();
然后您可以通过简单的依赖注入在应用程序的其他地方引用此 bean(如果引用组件是 Servlet、Listener、Filter、其他 EJB、JSF 托管 bean)。
@EJB
private Executor executor;
然后照常使用Executor
。
如果该组件不是另一个 Java EE 组件,您可以通过以下方式查找 bean:
InitialContext initialContext = new InitialContext();
Executor executor = (Executor) initialContext.lookup("java:module/Executor");
【讨论】:
略微 O/T:这是一个非常好的模式(我们也经常使用它),但是当您的算法需要单独的线程池来防止死锁时,它就不足了(请参阅 EJB 规范问题讨论这个也是) 我们在应用程序中使用相同的设计,但最近在使用执行程序在后台运行批量操作时,我们面临数据库中的大量数据覆盖。任何想法,如果这可能是一个常见/已知的问题。如果是,那么是否有可用的修复程序 正确的做法是使用ManagedExecutorService,我的回答是below 注意:对 EJB 的异步方法调用将在调用者的事务范围内执行(并将延长@RequestScope
实体的生命周期)。如果这不是您需要的,您可以使用 @TransactionAttribute(value= TransactionAttributeType.NEVER)
显式禁用此 EJB 上的事务支持
@romblen 您在哪个服务器上遇到过这种情况?根据规范,EJB 异步方法应在其自己的事务和安全上下文中执行。【参考方案2】:
嗯...大卫的解决方案对我不起作用,原因如下:
-
编译器一直在抱怨 java.util.concurrent 是不允许的……这在 JBOSS 范围内是有道理的。
还有:公共静态类...?阅读本文:Why are you not able to declare a class as static in Java?
这就是我所做的: 我的安装: - JBOSS AS 7.1.1 - Java 1.6 - RHEL - 使用Gradle 和Arquillian 运行示例:
@Stateless
public class ExecutorBean
@Asynchronous
public void execute(Runnable command)
command.run();
那么你的客户端是这样的:
@EJB ExecutorBean eb;
@Test
public void testExecutorBean()
eb.execute(new YourCustomizedRunnableWhichDoesALotOfUsefulStuff());
assertFalse(!true);
但请注意:在我的standalone.xml(或者一般来说,我的 JBOSS 配置文件中,我有一个“线程池”部分。看看它(如果您碰巧使用 JBOSSAS)并修改那里的值.找出它的行为。当我使用带有 arquillian 测试的线程时,尽管我的 keepalive-time 非常高,但我得到的线程被杀死了。我认为这与 arquillian 微部署的方式有关。当 arquillian 完成时,所有未完成的线程都被杀死在运行测试时正在运行...至少这是我认为我观察到的。另一方面,所有已完成的线程实际上表现良好,因为它们完成了他们的任务/操作。
希望这篇文章有帮助!
【讨论】:
我刚刚为我的测试方法设计了最优雅的解决方案:让主测试线程休眠一段时间,以便应用服务器管理的其他线程有时间完成。好的...不是那么优雅,但可以工作。 Yankedstatic
-- 我复制的代码是一个静态内部类。已删除,因为粘贴的代码旨在成为***类。【参考方案3】:
在 EJB 中执行此操作的正确方法是使用 ManagedExecutorService,它是 Concurrency Utils API (Java EE7) 的一部分。您不应在企业代码中使用属于 java.util.concurrent 的任何 ExecutorService。
通过使用 ManagedExecutorService,您的新线程将由容器创建和管理。
以下示例取自我的网站here。
要使用 ManagedExecutorService 创建一个新线程,首先要创建一个实现 Callable 的任务对象。在 call() 方法中,我们将定义我们希望在单独的线程中执行的工作。
public class ReportTask implements Callable<Report>
Logger logger = Logger.getLogger(getClass().getSimpleName());
public Report call()
try
Thread.sleep(3000);
catch (InterruptedException e)
logger.log(Level.SEVERE, "Thread interrupted", e);
return new Report();
然后我们需要通过将任务传递给 ManagedExecutorService 的 submit() 方法来调用该任务。
@Stateless
public class ReportBean
@Resource
private ManagedExecutorService executorService;
public void runReports()
ReportTask reportTask = new ReportTask();
Future<Report> future = executorService.submit(reportTask);
【讨论】:
这是 EE 7 中的首选方式,但 EE 6 呢? Java EE6 没有安全的方法来做到这一点。我想这就是它赢得投票被纳入 Java EE7 的原因。 谢谢...使用托管执行器绝对是要走的路。 如果您妥善管理ExecutorService
的初始化/关闭,那么有什么“危险”? ManagedExecutorService
实际上扩展 ExecutorService
。我知道的唯一问题是,当不再需要执行程序服务时,您需要关闭后备线程池,以便线程不会继续运行超过保证的时间。
@BasilBourque JSR 236 的主要目标之一是将 Java SE 线程与 EE 容器集成。容器中 SE 线程的主要缺点是: • 不受 Java EE 容器控制的线程 • 类加载器、安全性、命名等线程上下文无法传播 • 缺乏可管理性和事务隔离语义。有关详细信息,请参阅规范。 jcp.org/aboutJava/communityprocess/final/jsr236/index.html【参考方案4】:
在 EE7 之前,您可能希望使用 JSR 237 中的 WorkManager
http://docs.oracle.com/javaee/1.4/api/javax/resource/spi/work/WorkManager.html
此规范目前已撤销,但仍有一些应用服务器实施。 我在 WebSphere 8.5 中使用 ibm 实现 - IBM WorkManager。它是完全托管的资源,可在管理控制台中使用。请注意,它与 Oracle 的接口不兼容。
这是 IBM 版本的示例:
@Resource(lookup = "wm/default")
WorkManager workManager;
public void process()
try
ArrayList<WorkItem> workItems = new ArrayList<WorkItem>();
for (int i = 0; i < 100; i++)
// submit 100 jobs
workItems.add(workManager.startWork(new Work()
@Override
public void run()
try
System.out.println(Thread.currentThread().getName() + " Running");
Thread.sleep(1000);
catch (InterruptedException e)
e.printStackTrace();
@Override
public void release()
System.out.println(Thread.currentThread().getName() + " Released");
));
// wait for all jobs to be done.
workManager.join(workItems, WorkManager.JOIN_AND, 100000);
catch (WorkException e)
e.printStackTrace();
我也知道Commonj Workmanager。
【讨论】:
【参考方案5】:如果你使用 JBoss,你可以使用 org.jboss.seam.async.ThreadPoolDispatcher。
ThreadPoolDispatcher 是完全托管的。
有关其他有用的托管类,请参阅包:org.jboss.seam.async。
【讨论】:
以上是关于Jboss Java EE 容器和一个 ExecutorService的主要内容,如果未能解决你的问题,请参考以下文章