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 完成时,所有未完成的线程都被杀死在运行测试时正在运行...至少这是我认为我观察到的。另一方面,所有已完成的线程实际上表现良好,因为它们完成了他们的任务/操作。

希望这篇文章有帮助!

【讨论】:

我刚刚为我的测试方法设计了最优雅的解决方案:让主测试线程休眠一段时间,以便应用服务器管理的其他线程有时间完成。好的...不是那么优雅,但可以工作。 Yanked static -- 我复制的代码是一个静态内部类。已删除,因为粘贴的代码旨在成为***类。【参考方案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的主要内容,如果未能解决你的问题,请参考以下文章

linux服务器 jboss-7安装

四大Java EE容器

Java EE EJB 部署错误:容器已注册

中间件漏洞总结- jBoss

tomcat与jboss等容器的区别

转 四大Java EE容器(TomcatJBossResinGlassfish)之简单比较