如何判断线程池中的线程是否全部执行完毕
Posted 李晓LOVE向阳
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何判断线程池中的线程是否全部执行完毕相关的知识,希望对你有一定的参考价值。
在使用多线程执行任务的时候,在某些场景下需要判断线程池中所有线程是否都已执行完毕,例如:我有个方法的功能是往一个文件异步地写入内容,我需要在所有的子线程写入完毕后在文件末尾写“---END---”及关闭文件流等,这个时候我就需要某个标志位可以告诉我是否线程池中所有的子线程都已经执行完毕。下面我总结了两种方式来判断。
第一种方式:使用Semaphore
Semaphore简介:
Semaphore,是JDK1.5的java.util.concurrent并发包中提供的一个并发工具类。
所谓Semaphore即 信号量 的意思。
这个叫法并不能很好地表示它的作用,更形象的说法应该是许可证管理器。
其作用在JDK注释中是这样描述的:
A counting semaphore.
Conceptually, a semaphore maintains a set of permits.
Each {@link #acquire} blocks if necessary until a permit is available, and then takes it.
Each {@link #release} adds a permit, potentially releasing a blocking acquirer.
However, no actual permit objects are used; the {@code Semaphore} just keeps a count of the number available and acts accordingly.
翻译过来,就是:
Semaphore是一个计数信号量。
从概念上将,Semaphore包含一组许可证。
如果有需要的话,每个acquire()方法都会阻塞,直到获取一个可用的许可证。
每个release()方法都会释放持有许可证的线程,并且归还Semaphore一个可用的许可证。
然而,实际上并没有真实的许可证对象供线程使用,Semaphore只是对可用的数量进行管理维护。
了解更多Semaphore可以参考以下博客:
https://blog.csdn.net/hanchao5272/article/details/79780045
具体使用:
public class MySemaphore {
public static void main(String[] args) throws IOException, InterruptedException {
final File stream = new File("c:\\\\temp\\\\stonefeng\\\\stream.txt");
final OutputStream os = new FileOutputStream(stream);
final OutputStreamWriter writer = new OutputStreamWriter(os);
final Semaphore semaphore = new Semaphore(10);
ExecutorService exec = Executors.newCachedThreadPool();
final long start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
final int num = i;
Runnable task = new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
writer.write(String.valueOf(num)+"\\n");
semaphore.release();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
exec.submit(task);
}
exec.shutdown();
while(true){
if(exec.isTerminated()){
writer.write("---END---\\n");
writer.close();
System.out.println("所有的子线程都结束了!");
break;
}
Thread.sleep(1000);
}
final long end = System.currentTimeMillis();
System.out.println((end-start)/1000);
}
}
第二种方式:CountDownLatch
判断线程池中的线程是否全部执行完毕的另外一种解决方案则是使用闭锁(CountDownLatch)来实现,CountDownLatch是一种灵活的闭锁实现,它可以使一个或多个线程等待一组事件发生。闭锁状态包括一个计数器,该计数器被初始化为一个正数,表示需要等待的事件数量。countDown方法递减计数器,表示有一个事件已经发生了,而await方法等待计数器达到零,即表示需要等待的事情都已经发生。可以使用闭锁来这样设计程序达到目的
public class CountDownLatchApproach {
public static void main(String[] args) throws IOException, InterruptedException {
final int nThreads = 10;
final CountDownLatch endGate = new CountDownLatch(nThreads);
final File stream = new File("c:\\\\temp\\\\stonefeng\\\\stream.txt");
final OutputStream os = new FileOutputStream(stream);
final OutputStreamWriter writer = new OutputStreamWriter(os);
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < nThreads; i++) {
final int num = i;
Runnable task = new Runnable() {
@Override
public void run() {
try {
writer.write(String.valueOf(num)+"\\n");
} catch (IOException e) {
e.printStackTrace();
} finally {
endGate.countDown();
}
}
};
exec.submit(task);
}
endGate.await();
writer.write("---END---\\n");
writer.close();
}
}
这种解决方案虽然可以达到目的但是性能差到没朋友,我更倾向于使用第一种方案
以上是关于如何判断线程池中的线程是否全部执行完毕的主要内容,如果未能解决你的问题,请参考以下文章