共计 4471 个字符,预计需要花费 12 分钟才能阅读完成。
起源:https://blog.csdn.net/m0_4614…
概述
最近写小玩具的时候用到了 CountDownLatch
计数器,而后顺便想了想判断线程池全副完结有多少种办法。
在网上搜了下,可能有些没找到,然而我找到的有(所有办法都是在 ThreadPoolExecutor
线程池办法下测试的):
isTerminated()
判断形式,在执行shutdown()
,敞开线程池后,判断是否所有工作曾经实现。ThreadPoolExecutor
的getCompletedTaskCount()
办法,判断实现工作数和全副工作数是否相等。CountDownLatch
计数器,应用闭锁计数来判断是否全副实现。- 手动保护一个公共计数,原理和闭锁相似,就是更加灵便。
- 应用
submit
向线程池提交工作,Future
判断工作执行状态。
好嘞,当初开始一个一个介绍优缺点和简要原理;
先创立一个 static 线程池,前面好几个例子就不一一创立了,全副用这个就行了:
/**
* 创立一个最大线程数是 20 的线程池
*/
public static ThreadPoolExecutor pool = new ThreadPoolExecutor(
10, 20, 0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>());
而后再筹备一个通用的睡眠办法:
/**
* 线程执行办法,随机期待 0 到 10 秒
*/
private static void sleepMtehod(int index){
try {long sleepTime = new Double(Math.random() * 10000).longValue();
Thread.sleep(sleepTime);
System.out.println("以后线程执行完结:" + index);
} catch (InterruptedException e) {e.printStackTrace();
}
}
这个办法就是为了测试的时候辨别线程执行结束的下程序而已。
好嘞,筹备结束,当初开始。
isTerminated 形式
首先贴上测试代码:
private static void shutdownTest() throws Exception {for (int i = 0; i < 30; i++) {
int index = i;
pool.execute(() -> sleepMtehod(index));
}
pool.shutdown();
while (!pool.isTerminated()){Thread.sleep(1000);
System.out.println("还没进行。。。");
}
System.out.println("全副执行结束");
}
这一种形式就是在主线程中进行循环判断,全副工作是否曾经实现。
这里有两个次要办法:
shutdown()
:启动有序敞开,其中先前提交的工作将被执行,但不会承受任何新工作。如果曾经敞开,调用没有额定的作用。isTerminated()
:如果所有工作在敞开后实现,则返回 true。请留神,isTerminated
从不是 true,除非shutdown
或shutdownNow
先被执行。
艰深点讲,就是在执行全副工作后,对线程池进行 shutdown()
有序敞开,而后循环判断 isTerminated()
,线程池是否全副实现。
- 长处:操作简略,代码更加简略。
- 毛病:须要敞开线程池。个别我在代码中都是将线程池注入到 Spring 容器,而后各个组件中对立用同一个,当然不能敞开。
相似办法扩大:
shutdownNow()
:尝试进行所有被动执行的工作,进行期待工作的解决,并返回正在期待执行的工作列表。从此办法返回时,这些工作将从工作队列中删除。通过Thread.interrupt()
勾销工作。isShutdown()
:如果线程池已敞开,则返回 true。isTerminating()
:如果在shutdown()
或shutdownNow()
之后终止,但尚未齐全终止,则返回 true。waitTermination(long timeout, TimeUnit unit)
:以后线程阻塞,直到等所有已提交的工作(包含正在跑的和队列中期待的)执行完,或者等超时工夫到,或者线程被中断抛出异样;全副执行完返回 true,超时返回 false。也能够用这个办法代替isTerminated()
进行判断。
getCompletedTaskCount
还是一样,贴上代码:
private static void taskCountTest() throws Exception {for (int i = 0; i < 30; i++) {
int index = i;
pool.execute(() -> sleepMtehod(index));
}
// 当线程池实现的线程数等于线程池中的总线程数
while (!(pool.getTaskCount() == pool.getCompletedTaskCount())) {System.out.println("工作总数:" + pool.getTaskCount() + ";曾经实现工作数:" + pool.getCompletedTaskCount());
Thread.sleep(1000);
System.out.println("还没进行。。。");
}
System.out.println("全副执行结束");
}
还是一样在主线程循环判断,次要就两个办法:
getTaskCount()
:返回打算执行的工作总数。因为工作和线程的状态可能在计算过程中动态变化,因而返回的值只是一个近似值。getCompletedTaskCount()
:返回实现执行的工作的大抵总数。因为工作和线程的状态可能在计算过程中动静地扭转,所以返回的值只是一个近似值,然而在间断的调用中并不会缩小。
这个好了解,总任务数等于已实现工作数,就示意全副执行结束。
- 长处:齐全应用了
ThreadPoolExecutor
提供的办法,并且不用敞开线程池,防止了创立和销毁带来的损耗。 - 毛病:下面的解释也看到了,应用这种判断存在很大的限度条件;必须确定,在循环判断过程中,没有新的工作产生。差不多意思就是,这个线程池只能在这条线程中应用。
其余:
最初扯两句,因为我用 main 办法运行的,跑完后 main 没有完结,是因为非守护线程如果不终止,程序是不会完结的。而线程池 Worker 线程里写了一个死循环,而且被设置成了非守护线程。
CountDownLatch 计数器
这种办法是我比拟罕用的办法,先看代码:
private static void countDownLatchTest() throws Exception {
// 计数器,判断线程是否执行完结
CountDownLatch taskLatch = new CountDownLatch(30);
for (int i = 0; i < 30; i++) {
int index = i;
pool.execute(() -> {sleepMtehod(index);
taskLatch.countDown();
System.out.println("以后计数器数量:" + taskLatch.getCount());
});
}
// 以后线程阻塞,期待计数器置为 0
taskLatch.await();
System.out.println("全副执行结束");
}
这种办法,呃,应该是看起来比拟高级的,我也不晓得别的大佬怎么写的,反正我就用这个。
这个办法须要介绍下这个工具类 CountDownLatch
。先把这种形式的优缺点写了,前面再具体介绍这个类。
- 长处:代码优雅,不须要对线程池进行操作,将线程池作为 Bean 的状况下有很好的应用场景。
- 毛病:须要提前晓得线程数量;性能的确,呃呃呃呃呃,差了点。哦对了,还须要在线程代码块内加上异样判断,否则在
countDown
之前产生异样而没有解决,就会导致主线程永远阻塞在 await。
CountDownLatch 概述
CountDownLatch
是 JDK 提供的一个同步工具,它能够让一个或多个线程期待,始终等到其余线程中执行实现一组操作。
罕用的办法有 countDown
办法和 await
办法,CountDownLatch
在初始化时,须要指定用给定一个整数作为计数器。
当调用 countDown
办法时,计数器会被减 1;当调用 await
办法时,如果计数器大于 0 时,线程会被阻塞,始终到计数器被 countDown
办法减到 0 时,线程才会继续执行。
计数器是无奈重置的,当计数器被减到 0 时,调用 await 办法都会间接返回。
保护一个公共计数
这种形式其实和 CountDownLatch 原理相似。
先保护一个动态变量
private static int taskNum = 0;
而后在线程工作完结时,进行动态变量操作:
private static void staticCountTest() throws Exception {Lock lock = new ReentrantLock();
for (int i = 0; i < 30; i++) {
int index = i;
pool.execute(() -> {sleepMtehod(index);
lock.lock();
taskNum++;
lock.unlock();});
}
while(taskNum < 30) {Thread.sleep(1000);
System.out.println("还没进行。。。以后实现工作数:" + taskNum);
}
System.out.println("全副执行结束");
}
其实就是加锁计数,循环判断。
- 长处:手动保护形式更加灵便,对于一些非凡场景能够手动解决。
- 毛病:和
CountDownLatch
相比,一样须要晓得线程数目,然而代码实现比拟麻烦,绝对于灵便这一个劣势,貌似投入产出并不对等。
Future 判断工作执行状态
Future
是用来装载线程后果的,不过,用这个来进行判断写代码总感觉怪怪的。
因为 Future
只能装载一条线程的返回后果,多条线程总不能用 List 在接管 Future。
这里就开一个线程做个演示:
private static void futureTest() throws Exception {Future<?> future = pool.submit(() -> sleepMtehod(1));
while (!future.isDone()){Thread.sleep(500);
System.out.println("还没进行。。。");
}
System.out.println("全副执行结束");
}
这种形式就不写优缺点了,因为 Future
的次要应用场景并不是用于判断工作执行状态。
近期热文举荐:
1.1,000+ 道 Java 面试题及答案整顿 (2022 最新版)
2. 劲爆!Java 协程要来了。。。
3.Spring Boot 2.x 教程,太全了!
4. 别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!!
5.《Java 开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞 + 转发哦!