88

我在 Java 1.6 中使用 ExecutoreService,只需通过

ExecutorService pool = Executors.newFixedThreadPool(THREADS). 

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

pool.shutdown();

我可以通过某种方式将此池使用的内部线程管理转换为守护线程来避免调用它吗?或者我在这里错过了什么。

4

9 回答 9

114

可能最简单和首选的解决方案在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);

但是如果你想得到 executor 来完成它的任务,同时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();
        }
    }
});
于 2012-12-14T17:21:22.663 回答
58

已经有一个内置功能用于创建一个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必须超过几秒钟,Pshemo 的答案中的解决方案可能更合适:当线程设置为守护线程时,它们将在应用程序退出时立即结束.

于 2015-04-05T01:19:32.943 回答
23

我会使用 Guava 的 ThreadFactoryBuilder 类。

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

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

于 2015-04-08T16:35:06.230 回答
10

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

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

但是我通常希望我所有的 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;
    }
}

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

ExecutorService pool = Executors.newFixedThreadPool(
    4, DaemonThreadFactory.instance
);

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

DaemonThreadFactory.instance.newThread(
    () -> { doSomething(); }
).start();
于 2018-08-19T22:22:23.937 回答
5

是的。

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

于 2012-12-14T17:14:42.207 回答
0

此解决方案类似于@Marco13,但不是创建我们自己的ThreadPoolExecutor,而是可以修改由Executors#newFixedThreadPool(int nThreads). 就是这样:

ExecutorService ex = Executors.newFixedThreadPool(nThreads);
 if(ex instanceof ThreadPoolExecutor){
    ThreadPoolExecutor tp = (ThreadPoolExecutor) ex;
    tp.setKeepAliveTime(time, timeUnit);
    tp.allowCoreThreadTimeOut(true);
}
于 2018-09-09T18:17:05.983 回答
0

这并不能完全回答手头的问题,但它可能很有用:

正如其他人评论的那样,您可以创建 DaemonThreadFactory,内联或作为实用程序类。

通过继承 Runnable,您可以从上述工厂获取生成的 DaemonThread,以在单独生成的非守护线程中执行整个可运行单元。在正常情况下,即使 Executor 中使用的守护线程被指定为关闭,这个非守护线程也会完成。

这是一个小例子:

import static java.util.concurrent.TimeUnit.*;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.*;

public class ScheduleStackOverflow {

    private static final DateTimeFormatter DTF_HH_MM_SS = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");

    private static final class ThreadRunnable implements Runnable {

        private final Runnable runnable;

        public ThreadRunnable(final Runnable runnable) {
            this.runnable = runnable;
        }
        @Override
        public void run() {
            final Thread delegateThread = new Thread(this.runnable);
            /**/         delegateThread.setDaemon(false); // TODO Try changing this to "true"
            /**/         delegateThread.start();
        }
    }

    public static void main(final String[] args) throws InterruptedException {

        final     ThreadFactory daemonThreadFactory = (daemonRunnable) -> {
            final Thread        daemon = new Thread   (daemonRunnable);
            /**/                daemon.setDaemon(true);
            return              daemon;
        };

        final Runnable runnable = new ThreadRunnable(() -> {
            System.out.println(DTF_HH_MM_SS.format(LocalTime.now()) + " daemon=" + Thread.currentThread().isDaemon() + " Scheduling...");

            sleep(5, SECONDS);

            System.out.println(DTF_HH_MM_SS.format(LocalTime.now()) + " daemon=" + Thread.currentThread().isDaemon() + " Schedule done.");
        });

        Executors
        .newSingleThreadScheduledExecutor(daemonThreadFactory)
        .scheduleAtFixedRate(runnable, 0, Duration.ofSeconds(10).toNanos(), NANOSECONDS);

        sleep(12, SECONDS);

        System.out.println(DTF_HH_MM_SS.format(LocalTime.now()) + " Main CLOSED!");
    }

    private static void sleep(final long timeout, final TimeUnit timeunit) {
        try {timeunit.sleep(timeout);} catch (InterruptedException e) {}
    }
}
于 2021-03-21T11:08:03.900 回答
0

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

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;
    }
}
于 2019-07-04T20:39:29.690 回答
-1

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

当你的主线程完成后,使用 awaitTermination() 方法让提交的任务有时间完成。当前提交的任务将被执行,一旦完成,线程池将终止其控制线程。

for (Runnable task : tasks) {
  threadPool.submit(task);
}
threadPool.shutdown();
/*... do other stuff ...*/
//All done, ready to exit
while (!threadPool.isTerminated()) {
  //this can throw InterruptedException, you'll need to decide how to deal with that.
  threadPool.awaitTermination(1,TimeUnit.SECOND); 
}
于 2015-04-08T17:36:05.020 回答