Jboss Java EE容器和ExecutorService

eme*_*ava 27 java concurrency jboss multithreading ejb

我有一个独立的Java应用程序,它使用ExecutorService并行处理大量作业

 ExecutorService es = Executors.newFixedThreadPool(10);
Run Code Online (Sandbox Code Playgroud)

我现在想在EJB bean中重用相同的解决方案但不确定如何正确初始化ThreadPool,因为我通常会让Java EE容器控制所有线程资源.我可以使用相同的代码,还是有另一种正确的方法来获取Jboss托管线程池?

Chr*_*hie 30

在EJB中执行此操作的正确方法是使用ManagedExecutorService,它是Concurrency Utils API(Java EE7)的一部分.您不应该使用属于企业代码中java.util.concurrent的任何ExecutorService.

通过使用ManagedExecutorService,您的新线程将由容器创建和管理.

下面的例子是从我的网站采取了这里.

要使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我们需要通过将任务传递给ManagedExecutorService的submit()方法来调用该任务.

@Stateless
public class ReportBean {

    @Resource
    private ManagedExecutorService executorService;

    public void runReports() {
        ReportTask reportTask = new ReportTask();
        Future<Report> future = executorService.submit(reportTask);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是EE 7的首选方式,但EE 6呢? (6认同)

Dav*_*ins 26

强制警告:不鼓励在Java EE应用服务器(甚至Tomcat)中创建自己的线程,因为它可能是一个巨大的性能问题,并且在大多数情况下会阻止容器功能(如JNDI)工作.新线程将不知道它们属于哪个应用程序,不会设置Thread上下文类加载器以及许多其他隐藏的问题.

幸运的是,有一种方法可以让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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以通过普通依赖注入(如果引用组件是Servlet,Listener,Filter,其他EJB,JSF Managed bean)在您的应用程序的其他位置引用此bean.

@EJB
private Executor executor;
Run Code Online (Sandbox Code Playgroud)

然后使用Executor正常.

如果组件不是另一个Java EE组件,则可以通过以下方式查找bean:

InitialContext initialContext = new InitialContext();
Executor executor = (Executor) initialContext.lookup("java:module/Executor");
Run Code Online (Sandbox Code Playgroud)

  • 稍微O/T:这是一个非常好的模式(我们也使用它很多),但是当你的算法需要单独的线程池来防止死锁时,它就会失败(参见EJB Spec问题讨论) (2认同)
  • @rcomblen 你在哪个服务器遇到过这个问题?根据规范,EJB 异步方法应在其自己的事务和安全上下文中执行。 (2认同)

eas*_*Man 5

嗯......大卫的解决方案对我不起作用,原因如下:

  1. 编译器喋喋不休地说java.util.concurrent是不允许的......在JBOSS范围内哪种有意义.
  2. 另外:公共STATIC课......?阅读本文:为什么你不能在Java中将类声明为静态?

这就是我做的:
我的安装:
- JBOSS AS 7.1.1
- Java 1.6
- RHEL
- 使用GradleArquillian运行示例:

@Stateless
public class ExecutorBean {
    @Asynchronous
    public void execute(Runnable command) {
        command.run();    
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你的客户端看起来像这样:

@EJB ExecutorBean eb;
@Test
public void testExecutorBean() {
    eb.execute(new YourCustomizedRunnableWhichDoesALotOfUsefulStuff());
    assertFalse(!true);
}
Run Code Online (Sandbox Code Playgroud)

但请注意:在我的standalone.xml中(或者一般来说我的JBOSS配置文件中有一个'线程池'部分.看看它(如果你碰巧使用JBOSSAS)并修改那里的值.找出来它是如何表现的.当我使用带有arquillian测试的线程时,我得到的线程虽然我的保持时间非常高但已被杀死.我认为这与arquillian microdeploys有关.当arquillian完成所有未完成的线程被杀死时正在运行测试正在进行中......至少这是我认为我观察到的.另一方面,所有完成的线程实际上表现得很好,他们完成了任务/操作.

希望这篇文章有帮助!