我应该在后端(java)使用WorkManager进行多线程处理吗?

我应该在后端(java)使用WorkManager进行多线程处理吗?,java,multithreading,Java,Multithreading,我在java web应用程序的后端工作。我添加了一些多线程来加速大数据检索,并使用了在此过程中创建的一些ExecutorServices。然而,我已经读到,在web应用程序中以这种方式创建线程可能不是一个好主意,而“com.ibm.websphere.asynchbeans.WorkManager”可能是一个选项。不过,在后端使用它似乎不是很友好。根据文档,WorkManager是“为使用异步bean的Java平台企业版(JavaEE)应用程序创建的线程池”。一、 作为一个非常前端无知的后端家伙

我在java web应用程序的后端工作。我添加了一些多线程来加速大数据检索,并使用了在此过程中创建的一些ExecutorServices。然而,我已经读到,在web应用程序中以这种方式创建线程可能不是一个好主意,而“com.ibm.websphere.asynchbeans.WorkManager”可能是一个选项。不过,在后端使用它似乎不是很友好。根据文档,WorkManager是“为使用异步bean的Java平台企业版(JavaEE)应用程序创建的线程池”。一、 作为一个非常前端无知的后端家伙,甚至不完全知道豆子是什么。看起来工作管理器并不是我想要的,但如果手动创建ExecutorService实际上是个坏主意,我不确定最好的方法是什么。

在webapp的后端创建自己的线程是个坏主意,原因如下:Application Server container为您管理线程。因此,如果您在应用程序中创建自己的线程,容器将不知道这些线程,也无法管理它们。因此,如果对线程管理不当,应用程序可能会导致内存泄漏和其他与线程相关的问题。理论上,最好的方法是在应用服务器中注册一些线程池,并使用JNDI从容器中请求线程。但这可能有点过分了。因此,有时,您可能需要管理自己的线程。因此,在本例中,
ExecutorService
是最好的方法,因为它提供了非常好的API来管理线程。只需确保在应用程序关闭时关闭
ExecutorService
,这样就不会留下孤立线程。如果您对此很小心,那么您可以创建自己的线程。只需谨慎使用它们,并在完成或应用程序关闭时小心关闭
ExecutorService
。顺便说一句,
ThreadLocal
变量也存在类似的问题。完成后,您必须在其中调用metod
remove()
,否则即使您的应用程序关闭,它们也会保留在内存中,只有应用程序服务器重新启动才会清除它们。这是一个危险的内存泄漏

进一步介绍Michael Gantman的答案,这是一个很好的解释,允许容器管理线程池的JavaEE方法实际上非常容易实现:

@Stateless
public class Foo {

    @Asynchronous
    @Override
    public void doSomething() {
        //all calls to this function are asynchronous
    }
}
@Aysnchronous
处理了这里的魔法,它向容器指定应该异步调用此函数,容器负责弄清楚这意味着什么。然后调用容器管理的线程池来执行您的函数非常简单,如下所示:

@Stateless
public class Bar {
    @EJB
    Foo myFooEJB;

    public void businessMethod() {
        myFooEJB.doSomething();
    }
}
签出这些以获取更多信息:。可能有点过于详细,因为我知道您使用的是IBM产品,在JBoss中,您的异步配置作为子系统配置的一部分,通过指定
)
其中
poolName
应引用以下池:

<thread-pools>
    <thread-pool name="poolName">
        <max-threads count="10"/>
        <keepalive-time time="100" unit="milliseconds"/>
    </thread-pool>
</thread-pools>

但是,如果您试图提交,有一种更好的方法,只需将某个任务提交给可由容器管理的:

@Resource
ManagedExecutorService executorService;

@Resource
ManagedScheduledExecutorService scheduledExecutorService;

java.io.PrintWriter out = ...;

void scheduleSomeStuff(){
    scheduledExecutorService.schedule(new Runnable() {
        @Override
        public void run() {
            out.println("Print out after roughly 15 seconds.")
        }
    }, 15, TimeUnit.SECONDS);

    executorService.submit(new Callable<Boolean>() {
        @Override
        public Boolean call() throws Exception {
            out.println("Print only once, when the task is run by the executor service's discretion.");
        }
    };
}
@Resource
ManagedExecutorService执行器服务;
@资源
ManagedScheduledExecutorService scheduledExecutorService;
java.io.PrintWriter out=。。。;
void scheduleSomeStuff(){
scheduledExecutorService.schedule(新的Runnable(){
@凌驾
公开募捐{
println(“大约15秒后打印出来”)
}
},15,时间单位为秒);
executorService.submit(新的可调用(){
@凌驾
公共布尔调用()引发异常{
println(“当执行者服务自行决定运行任务时,仅打印一次。”);
}
};
}
这两个资源,特别是我在这里引用的资源,运行在不同的线程池上。当时,我最初在这里发布了一些东西,使用
@Asynchronous
线程池作为执行器,但这是不必要的,因为容器应该已经为您提供了一个线程池;因此,您现在可以看到更新的答案

同样,这可能有点特定于JBoss,但在JBoss中,这被配置为子系统
托管计划执行器服务
托管执行器服务

编辑2020年4月

所以我不知何故又找到了这个答案,重新阅读了一遍,我对自己没有明确地调用EJB规范感到非常困惑。这样做不仅是个坏主意,而且如果您创建自己的线程,您将直接违反EJB规范

JSR345:EnterpriseJavaBeans,版本3.2EJB核心契约和需求

第16.2.2节Bean提供者的责任、编程限制:

  • 企业bean不得尝试管理线程。企业bean不得尝试启动、停止、挂起或恢复线程,或更改线程的优先级或名称。企业bean不得尝试管理线程组
这些函数是为EJB容器保留的。允许企业bean管理线程会降低容器正确管理运行时环境的能力


换句话说,永远不要试图在web应用程序中管理自己的线程。没有理由尝试在web应用程序中创建/管理您自己的线程;也没有理由这样做。

我建议创建一个单吨类,该类将有一个执行器服务,池中有固定数量的线程,比如10个。每次需要创建线程时,调用该单吨类并将任务传递给该类执行者服务来执行它。这样你就不会