CountDownLatch和CycliBarrier介绍
一、CountDownLatch
它被用来同步一个或多个任务,强制他们等待其他任务完成,这就是闭锁。
public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); }
类中只有一个构造函数,一个int类型的参数count,代表计数器。这个计数器的初始值是线程的数量,每当一个线程结束,count-1,当count==0 时,所有线程执行完毕,在闭锁上等待的线程就可以执行了。
类中还包含了三个公共方法:
public void await()
public boolean await(long timeout, TimeUnit unit)
public void countDown()
当每个任务完成时,都会调用countDown()方法。而等待问题被解决的任务在这个锁存器上调用await()方法,这个任务就相当于被挂起了直到 timeout 或 计数器为0 。
public class CountDownLatchDemo { static final int SIZE = 5; public static void main(String[] args) { ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(5)); CountDownLatch countDownLatch = new CountDownLatch(SIZE); pool.execute(new WaitTask(countDownLatch)); for (int i = 0; i < SIZE; i++) { pool.execute(new TaskNow(countDownLatch)); } } } class TaskNow implements Runnable { private final CountDownLatch countDownLatch; TaskNow(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { try { TimeUnit.MILLISECONDS.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("我没完事你别想跑"); //调用countDown()的方法会减少count次数直到为0,调用await的任务才能进行 countDownLatch.countDown(); } } class WaitTask implements Runnable { private final CountDownLatch countDownLatch; WaitTask(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { try { //调用await的任务会被挂起 countDownLatch.await(); System.out.println("终于轮到我了"); } catch (InterruptedException e) { e.printStackTrace(); } } }
结果:
我没完事你别想跑
我没完事你别想跑
我没完事你别想跑
我没完事你别想跑
我没完事你别想跑
终于轮到我了
二、CylicBarrier
栅栏类似于闭锁,只是要等到所有线程都到达栅栏,才能进行接下来的动作,在没到达栅栏之前先到的要等待。
构造方法
共有两个构造方法:
public CyclicBarrier(int parties, Runnable barrierAction) { if (parties <= 0) throw new IllegalArgumentException(); this.parties = parties; this.count = parties; this.barrierCommand = barrierAction; } public CyclicBarrier(int parties) { this(parties, null); }
CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程使用await()方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。
CyclicBarrier的另一个构造函数CyclicBarrier(int parties, Runnable barrierAction),用于线程到达屏障时,优先执行barrierAction,方便处理更复杂的业务场景。
await()方法
线程调用await()方法表示已经到达同步点,然后当前线程被阻塞。直到parties个参与线程调用了await()方法,CyclicBarrier同样提供带超时时间的await和不带超时时间的await方法:
public int await() throws InterruptedException, BrokenBarrierException { try { return dowait(false, 0L); } catch (TimeoutException toe) { throw new Error(toe); // cannot happen } } public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException{...}
可以看到await方法调用了dowait()方法,dowait()方法是核心方法其内容是:如果这个线程不是最后一个到达的线程那么进行等待直到:
- 最后一个线程到达,即index == 0
- 某个参与线程等待超时
- 某个参与线程被中断
- 调用了CyclicBarrier的reset()方法。该方法会将屏障重置为初始状态
案例
public class CycliBarrierDemo { public static void main(String[] args) { int threadCount = 3; CyclicBarrier cyclicBarrier = new CyclicBarrier(threadCount); ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1)); for (int i = 0; i < threadCount; i++) { pool.execute(new Horse(cyclicBarrier, i)); } } } class Horse implements Runnable { private CyclicBarrier cyclicBarrier; private int num; public Horse(CyclicBarrier cyclicBarrier, int num) { this.cyclicBarrier = cyclicBarrier; this.num = num; } @Override public void run() { System.out.println("马匹:" + num + "到了"); try { cyclicBarrier.await(); System.out.println("马匹:" + num + "在等待"); TimeUnit.MILLISECONDS.sleep(2000); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } System.out.println("马匹到齐"); } }
结果:
马匹:0到了
马匹:2到了
马匹:1到了
马匹:1在等待
马匹:0在等待
马匹:2在等待
马匹到齐
马匹到齐
马匹到齐