在上一篇文章中说过使用thread.join()方法、newSingleThreadExecutor单线程池来控制线程执行顺序。在文章的末尾我提出了一种构想,可否使用经典的生产者和消费者模型来控制执行顺序。在本文中,我将使用CountDownLatch来解决这个问题。
上图是countDownLatch的原理示意图。官方文档给出的解释是:CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程执行完后再执行。上图中线程A调用方法await()之后,进行阻塞,然后线程1执行任务完毕,数量减1,到最后线程2、3执行完毕,count=0。那么此时线程A等待了所有的线程执行完毕了任务。线程A状态复位,开始继续执行任务。
实现过程如下:
public class ThreadOrderTest { public static void main(String[] args) { /** * 创建线程类的时候,将上一个计数器和本线程计数器传入。运行前业务执行上一个计数器.await, 执行后本计数器.countDown。 */ CountDownLatch num0 = new CountDownLatch(0);// 在这里count为0,表示该线程立马复位执行 CountDownLatch num1 = new CountDownLatch(1);// 在这里count为0,表示该线程立马复位执行 CountDownLatch num2 = new CountDownLatch(1);// 在这里count为0,表示该线程立马复位执行 Thread t1 = new Thread(new Count(num0, num1)); Thread t2 = new Thread(new Count(num1, num2)); Thread t3 = new Thread(new Count(num2, num2)); t1.start(); t2.start(); t3.start(); } static class Count implements Runnable { CountDownLatch num1; CountDownLatch num2; /** * 该构造器传递了上一个线程的计数器和当前线程的计数器 * * @param num0 * @param num1 */ public Count(CountDownLatch num1, CountDownLatch num2) { super(); this.num1 = num1; this.num2 = num2; } @Override public void run() { try { // 等待线程1执行完毕线程2开始执行,因为线程1开始立马就会执行(count=0) num1.await(); System.out.println("开始执行线程:" + Thread.currentThread().getName()); num2.countDown();// 本线程计数器减少 } catch (InterruptedException e) { e.printStackTrace(); } } } }
具体是怎么实现的呢:首先在创建线程的时候传递了Runnable对象,而该对象设置了两个参数,第一个参数保存了前一个线程的计数器,第二个线程保存了当前线程的计数器。线程1初始化的时候设置了线程的计数器为0,也就是说会立马执行任务,执行完毕,线程2 的计数器调用countdown()方法,立马从初始化的1变为0,同样开始执行,那么此时执行完毕,线程3计数器也减去1,开始执行任务。这样保证了三个线程的执行顺序。
下面是执行的结果:
开始执行线程:Thread-0开始执行线程:Thread-1开始执行线程:Thread-2
要说明的是,CountDownLatch存在不足的地方在于,要创建该对象的时候,我们会传递计数器的初始值,但是这个值一经设置,就再也无法修改了,因此计数器的使用时一次性的。这就是java.util.CountDownLatch的不足之处了吧!
原文地址:https://www.cnblogs.com/gosaint/p/9557662.html
时间: 2024-10-09 11:00:43