【问题标题】:Jboss Java EE container and an ExecutorServiceJboss Java EE 容器和一个 ExecutorService
【发布时间】:2012-12-05 14:33:51
【问题描述】:

我有一个独立的 java 应用程序,它使用 ExecutorService 来并行处理多个作业

 ExecutorService es = Executors.newFixedThreadPool(10);

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

【问题讨论】:

    标签: java multithreading concurrency jboss ejb


    【解决方案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】:

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

    1. 编译器一直在抱怨 java.util.concurrent 是不允许的……这在 JBOSS 范围内是有道理的。
    2. 还有:公共静态类...?阅读本文:Why are you not able to declare a class as static in Java?

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

    @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。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-12-14
        • 1970-01-01
        • 2010-09-17
        • 2013-06-15
        • 2014-11-16
        • 1970-01-01
        • 2011-06-09
        • 1970-01-01
        相关资源
        最近更新 更多