我正在Java 1.6中使用ExecutoreService,其启动方式仅由
ExecutorService pool = Executors.newFixedThreadPool(THREADS).
当我的主线程完成时(以及线程池处理的所有任务),该池将阻止我的程序关闭,直到我显式调用
pool.shutdown();
是否可以通过某种方式将此池使用的内部线程管理转换为守护线程来避免调用此方法?还是我在这里想念什么。
我正在Java 1.6中使用ExecutoreService,其启动方式仅由
ExecutorService pool = Executors.newFixedThreadPool(THREADS).
当我的主线程完成时(以及线程池处理的所有任务),该池将阻止我的程序关闭,直到我显式调用
pool.shutdown();
是否可以通过某种方式将此池使用的内部线程管理转换为守护线程来避免调用此方法?还是我在这里想念什么。
keepAliveTime
根据任务执行的频率来确定合理的价值。
Answers:
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);
但是,如果您想让执行程序完成任务的执行程序,并同时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();
}
}
});
已经有一个内置的功能来创建一个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答案中的解决方案可能更合适:如果将线程设置为守护程序线程,则它们将在应用程序退出时立即结束。
shutdown
方法中所说的“关闭” 。我改写了一下,希望它现在不再那么模棱两可了。
main()
方法完成。后任务完成,ExecutorService的能活KeepAliveTime的,等到所有核心线程死亡。这意味着,当您尝试正常停止Java应用程序时,您需要等待一些(可能很大)的时间。我认为这不好。@Pshemo我相信您的回答是目前最好的。
keepAliveTime
几秒钟的时间(顺便说一下,很难想象什么时候大于几秒钟才是合适的,甚至是必要的)。如果要求它更大keepAliveTime
并且仍然立即退出,则最好使用守护程序线程解决方案。
ExecutorService
与allowCoreThreadTimeOut(true)
和keepAliveTime
足够小让它或多或少迅速死亡。这样有什么好处ExecutorService
?闲置一小段时间后,所有核心线程都会死亡,并且提交新任务将导致创建新线程以及所有开销,因此我的任务将不会立即开始。它将不再是一个游泳池。
我将使用番石榴的ThreadFactoryBuilder类。
ExecutorService threadPool = Executors.newFixedThreadPool(THREADS, new ThreadFactoryBuilder().setDaemon(true).build());
如果您尚未使用Guava,我将使用ThreadFactory子类,如Pshemo答案顶部所述
如果只想在一个地方使用它,则可以内联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;
}
);
但是我通常希望所有的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.instance
到ExecutorService
,例如
ExecutorService pool = Executors.newFixedThreadPool(
4, DaemonThreadFactory.instance
);
或使用它轻松地从中启动守护进程线程Runnable
,例如
DaemonThreadFactory.instance.newThread(
() -> { doSomething(); }
).start();
此解决方案类似于@ 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);
}
newFixedThreadPool
会返回ThreadPoolExecutor
。(确实如此,而且几乎肯定会永远如此,但您不知道那
您可以使用Guava的ThreadFactoryBuilder。我不想添加依赖关系,我想要Executors.DefaultThreadFactory的功能,所以我使用了composition:
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;
}
}
如果您有任务的已知列表,则根本不需要守护线程。提交所有任务后,您只需在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);
}