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

问题描述 投票:61回答:7

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

ExecutorService pool = Executors.newFixedThreadPool(THREADS). 

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

pool.shutdown();

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

java multithreading threadpool executorservice java-threads
7个回答
91
投票

可能最简单和首选的解决方案是在Marco13's answer,所以不要被投票差异所欺骗(我的回答是几年之久)或接受标记(它只是意味着我的解决方案适合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);

但是如果你想获得执行器,它将完成它的任务,同时在应用程序完成时自动调用它的shutdown()方法,你可能想用Guava's 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();
        }
    }
});

42
投票

已经有一个内置的功能来创建一个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;
}

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


19
投票

我会使用Guava的ThreadFactoryBuilder类。

ExecutorService threadPool = Executors.newFixedThreadPool(THREADS, new ThreadFactoryBuilder().setDaemon(true).build());

如果你还没有使用Guava,我会选择一个类似于Pshemo's answer顶部描述的ThreadFactory子类


4
投票

是。

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


3
投票

如果您只想在一个地方使用它,那么您可以在线使用qazxsw poi实现,例如对于具有4个线程的池,您将编写(示例显示为假定Java 1.8或更新版本的lambda):

java.util.concurrent.ThreadFactory

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

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

这让我可以轻松地将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; } } 传递到DaemonThreadFactory.instance,例如

ExecutorService

或者用它来轻松地从ExecutorService pool = Executors.newFixedThreadPool( 4, DaemonThreadFactory.instance ); 启动一个守护程序线程,例如:

Runnable

0
投票

这个解决方案与@ Marco13类似,但我们可以修改DaemonThreadFactory.instance.newThread( () -> { doSomething(); } ).start(); 返回的解决方案,而不是创建我们自己的ThreadPoolExecutor。这是如何做:

Executors#newFixedThreadPool(int nThreads)

0
投票

你可以使用Guava的ThreadFactoryBuilder。我不想添加依赖项,我想要Executors.DefaultThreadFactory的功能,所以我使用了组合:

ExecutorService ex = Executors.newFixedThreadPool(nThreads);
 if(ex instanceof ThreadPoolExecutor){
    ThreadPoolExecutor tp = (ThreadPoolExecutor) ex;
    tp.setKeepAliveTime(time, timeUnit);
    tp.allowCoreThreadTimeOut(true);
}

-1
投票

如果您有已知的任务列表,则根本不需要守护程序线程。您可以在提交所有任务后在ExecutorService上调用shutdown()。

当主线程完成时,使用awaitTermination()方法为已提交任务的完成留出时间。当前提交的任务将被执行,线程池将在完成后终止其控制线程。

class DaemonThreadFactory implements ThreadFactory {
    final ThreadFactory delegate;

    DaemonThreadFactory() {
        this(Executors.defaultThreadFactory());
    }

    DaemonThreadFactory(ThreadFactory delegate) {
        this.delegate = delegate;
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread thread = delegate.newThread(r);
        thread.setDaemon(true);
        return thread;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.