如何判断线程池任务已执行完?( 二 )


文章插图
(3)CountDownLatch和CyclicBarrierCountDownLatch 和 CyclicBarrier 类似,都是等待所有任务到达某个点之后,再进行后续的操作,如下图所示:

如何判断线程池任务已执行完?

文章插图
CountDownLatch 使用的示例代码如下:
【如何判断线程池任务已执行完?】public static void main(String[] args) throws InterruptedException {// 创建线程池ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20,0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));final int taskCount = 5;// 任务总数// 单次计数器CountDownLatch countDownLatch = new CountDownLatch(taskCount); // ①// 添加任务for (int i = 0; i < taskCount; i++) {final int finalI = i;threadPool.submit(new Runnable() {@Overridepublic void run() {try {// 随机休眠 0-4sint sleepTime = new Random().nextInt(5);TimeUnit.SECONDS.sleep(sleepTime);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(String.format("任务%d执行完成", finalI));// 线程执行完,计数器 -1countDownLatch.countDown();// ②}});}// 阻塞等待线程池任务执行完countDownLatch.await();// ③// 线程池执行完System.out.println();System.out.println("线程池任务执行完成!");}
代码说明:以上代码中标识为 ①、②、③ 的代码行是核心实现代码,其中:① 是声明一个包含了 5 个任务的计数器;② 是每个任务执行完之后计数器 -1;③ 是阻塞等待计数器 CountDownLatch 减为 0,表示任务都执行完了,可以执行 await 方法后面的业务代码了 。
以上程序的执行结果如下:
如何判断线程池任务已执行完?

文章插图
缺点分析CountDownLatch 缺点是计数器只能使用一次,CountDownLatch 创建之后不能被重复使用 。CyclicBarrier 和 CountDownLatch 类似,它可以理解为一个可以重复使用的循环计数器,CyclicBarrier 可以调用 reset 方法将自己重置到初始状态,CyclicBarrier 具体实现代码如下:
public static void main(String[] args) throws InterruptedException {// 创建线程池ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20,0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));final int taskCount = 5;// 任务总数// 循环计数器 ①CyclicBarrier cyclicBarrier = new CyclicBarrier(taskCount, new Runnable() {@Overridepublic void run() {// 线程池执行完System.out.println();System.out.println("线程池所有任务已执行完!");}});// 添加任务for (int i = 0; i < taskCount; i++) {final int finalI = i;threadPool.submit(new Runnable() {@Overridepublic void run() {try {// 随机休眠 0-4sint sleepTime = new Random().nextInt(5);TimeUnit.SECONDS.sleep(sleepTime);System.out.println(String.format("任务%d执行完成", finalI));// 线程执行完cyclicBarrier.await(); // ②} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}});}}以上程序的执行结果如下:
如何判断线程池任务已执行完?

文章插图
方法说明CyclicBarrier 有 3 个重要的方法:
  1. 构造方法:构造方法可以传递两个参数,参数 1 是计数器的数量 parties,参数 2 是计数器为 0 时,也就是任务都执行完之后可以执行的事件(方法) 。
  2. await 方法:在 CyclicBarrier 上进行阻塞等待,当调用此方法时 CyclicBarrier  的内部计数器会 -1,直到发生以下情形之一:
  1. 在 CyclicBarrier 上等待的线程数量达到 parties,也就是计数器的声明数量时,则所有线程被释放,继续执行 。
  2. 当前线程被中断,则抛出 InterruptedException 异常,并停止等待,继续执行 。
  3. 其他等待的线程被中断,则当前线程抛出 BrokenBarrierException 异常,并停止等待,继续执行 。
  4. 其他等待的线程超时,则当前线程抛出 BrokenBarrierException 异常,并停止等待,继续执行 。
  5. 其他线程调用 CyclicBarrier.reset() 方法,则当前线程抛出 BrokenBarrierException 异常,并停止等待,继续执行 。
  1. reset 方法:使得CyclicBarrier回归初始状态,直观来看它做了两件事:
  2. 如果有正在等待的线程,则会抛出 BrokenBarrierException 异常,且这些线程停止等待,继续执行 。
  3. 将是否破损标志位 broken 置为 false 。


    推荐阅读