在Java中将ExecutorService转换为守护进程

Ant*_*tiz 68 java multithreading executorservice threadpool java-threads

我在Java 1.6中使用ExecutoreService,简单地开始

ExecutorService pool = Executors.newFixedThreadPool(THREADS). 
Run Code Online (Sandbox Code Playgroud)

当我的主线程完成时(以及线程池处理的所有任务),此池将阻止我的程序关闭,直到我显式调用

pool.shutdown();
Run Code Online (Sandbox Code Playgroud)

我可以避免不得不通过某种方式将此池使用的内部线程管理变为deamon线程来调用它吗?或者我在这里遗漏了一些东西.

Psh*_*emo 94

可能最简单和首选的解决方案是在Marco13的答案中,所以不要被投票差异(我的回答是几年之久)或接受标记所迷惑(它只是意味着我的解决方案适合OP情况而不是最好).


您可以使用ThreadFactory将Executor中的线程设置为守护进程.这将影响执行程序服务,它也将成为守护程序线程,因此如果没有其他非守护程序线程,它(以及由它处理的线程)将停止.这是一个简单的例子:

ExecutorService exec = Executors.newFixedThreadPool(4,
        new ThreadFactory() {
            public Thread newThread(Runnable r) {
                Thread t = Executors.defaultThreadFactory().newThread(r);
                t.setDaemon(true);
                return t;
            }
        });

exec.execute(YourTaskNowWillBeDaemon);
Run Code Online (Sandbox Code Playgroud)

但是如果你想获得执行程序,它将完成任务,并且同时shutdown()在应用程序完成时自动调用它的方法,你可能想要用Guava 包装你的执行程序MoreExecutors.getExitingExecutorService.

ExecutorService exec = MoreExecutors.getExitingExecutorService(
        (ThreadPoolExecutor) Executors.newFixedThreadPool(4), 
        100_000, TimeUnit.DAYS//period after which executor will be automatically closed
                             //I assume that 100_000 days is enough to simulate infinity
);
//exec.execute(YourTask);
exec.execute(() -> {
    for (int i = 0; i < 3; i++) {
        System.out.println("daemon");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
});
Run Code Online (Sandbox Code Playgroud)


Mar*_*o13 46

已经存在一个内置功能,用于创建ExecutorService在一段时间不活动后终止所有线程:您可以创建一个ThreadPoolExecutor,传递所需的时间信息,然后调用allowCoreThreadTimeout(true)此执行器服务:

/**
 * Creates an executor service with a fixed pool size, that will time 
 * out after a certain period of inactivity.
 * 
 * @param poolSize The core- and maximum pool size
 * @param keepAliveTime The keep alive time
 * @param timeUnit The time unit
 * @return The executor service
 */
public static ExecutorService createFixedTimeoutExecutorService(
    int poolSize, long keepAliveTime, TimeUnit timeUnit)
{
    ThreadPoolExecutor e = 
        new ThreadPoolExecutor(poolSize, poolSize,
            keepAliveTime, timeUnit, new LinkedBlockingQueue<Runnable>());
    e.allowCoreThreadTimeOut(true);
    return e;
}
Run Code Online (Sandbox Code Playgroud)

编辑参考注释中的注释:请注意,当应用程序退出时,此线程池执行程序不会自动关闭.执行程序将在应用程序退出后继续运行,但不会超过keepAliveTime.如果,根据精确的应用程序要求,keepAliveTime必须超过几秒钟,Pshemo答案中的解决方案可能更合适:当线程设置为守护程序线程时,它们将在应用程序退出时立即结束.

  • @ Marco13这是一个有趣的功能,我不知道,但我认为我没有正确解决问题.假设有人使用您的方法创建一个_ExecutorService_并向其提交一些*task*,然后`main()`方法结束.*task*完成后,_ExecutorService_将为_keepAliveTime_生效,等待所有核心线程死亡.这意味着,当您尝试优雅地停止Java应用程序时,您需要等待一些(可能很大)的时间.我觉得这不好.@Pshemo我相信你的答案目前是最好的. (3认同)

Phi*_*ard 20

我会使用Guava的ThreadFactoryBuilder类.

ExecutorService threadPool = Executors.newFixedThreadPool(THREADS, new ThreadFactoryBuilder().setDaemon(true).build());
Run Code Online (Sandbox Code Playgroud)

如果您还没有使用Guava,我会选择一个ThreadFactory子类,如Pshemo的答案顶部所述

  • 它更简洁.如果您已经在使用Guava,为什么要重写库中存在的代码?你是对的,它完全一样. (4认同)

isa*_*pir 7

如果您只想在一个地方使用它,那么您可以内联java.util.concurrent.ThreadFactory实现,例如,对于您将编写的具有 4 个线程的池(示例显示为假设 Java 1.8 或更高版本的 lambda):

ExecutorService pool = Executors.newFixedThreadPool(4,
        (Runnable r) -> {
            Thread t = new Thread(r);
            t.setDaemon(true);
            return t;
        }
);
Run Code Online (Sandbox Code Playgroud)

但是我通常希望我所有的 Thread 工厂都生成守护线程,所以我添加了一个实用程序类,如下所示:

import java.util.concurrent.ThreadFactory;

public class DaemonThreadFactory implements ThreadFactory {

    public final static ThreadFactory instance = 
                    new DaemonThreadFactory();

    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    }
}
Run Code Online (Sandbox Code Playgroud)

这让我可以轻松地传递DaemonThreadFactory.instanceExecutorService,例如

ExecutorService pool = Executors.newFixedThreadPool(
    4, DaemonThreadFactory.instance
);
Run Code Online (Sandbox Code Playgroud)

或使用它轻松地从 启动守护进程线程Runnable,例如

DaemonThreadFactory.instance.newThread(
    () -> { doSomething(); }
).start();
Run Code Online (Sandbox Code Playgroud)


SLa*_*aks 5

是的。

您只需要创建自己的ThreadFactory类来创建守护线程而不是常规线程。