CountDownLatch
倒数计数器 一个线程等待其他所有线程
一.CountDownLatch用法
CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。
- await() throws InterruptedException:调用该方法的线程等到构造方法传入的N减到0的时候,才能继续往下执行;
- await(long timeout, TimeUnit unit):与上面的await方法功能一致,只不过这里有了时间限制,调用该方法的线程等到指定的timeout时间后,不管N是否减至为0,都会继续往下执行;
- countDown():使CountDownLatch初始值N减1;
- long getCount():获取当前CountDownLatch维护的值;
package ThreadLearn; import java.util.concurrent.CountDownLatch; public class CountDownLatchTest { /*** * * public void await() throws InterruptedException { }; * //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行 * public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; * //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行 * public void countDown() { }; //将count值减1 * * */ public static void main(String[] args) { final CountDownLatch latch = new CountDownLatch(2); new Thread(){ public void run() { try { System.out.println("子线程"+Thread.currentThread().getName()+"正在执行"); Thread.sleep(3000); System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); new Thread(){ public void run() { try { System.out.println("子线程"+Thread.currentThread().getName()+"正在执行"); Thread.sleep(3000); System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); try { System.out.println("等待2个子线程执行完毕..."); latch.await(); System.out.println("2个子线程已经执行完毕"); System.out.println("继续执行主线程"); } catch (InterruptedException e) { e.printStackTrace(); } } }
Cyclicbarrier 循环栅栏
栅栏类似于闭锁,它能阻塞一组线程直到某个事件的发生。栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程。
CyclicBarrier可以使一定数量的线程反复地在栅栏位置处汇集。当线程到达栅栏位置时将调用await方法,这个方法将阻塞直到所有线程都到达栅栏位置。如果所有线程都到达栅栏位置,那么栅栏将打开,此时所有的线程都将被释放,而栅栏将被重置以便下次使用。
CountDownLatch只有一次计数 ,
而Cyclicbarrier多次循环计数
//其参数表示屏障拦截的线程数量,每个线程使用await()方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。
await() throws InterruptedException, BrokenBarrierException
//与上面的await方法功能基本一致,只不过这里有超时限制,阻塞等待直至到达超时时间为止
await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException
//获取当前有多少个线程阻塞等待在临界点上
int getNumberWaiting()
//用于查询阻塞等待的线程是否被中断
boolean isBroken()
//将屏障重置为初始状态。如果当前有线程正在临界点等待的话,将抛出
BrokenBarrierException。 void reset()
public CyclicBarrier(int parties, Runnable barrierAction)//用于线程到达屏障时,优先执行barrierAction,方便处理更复杂的业务场景。
dowait(boolean, long)方法,它也是CyclicBarrier的核心方法,该方法定义如下:
private int dowait(boolean timed, long nanos) throws InterruptedException, BrokenBarrierException, TimeoutException { // 获取独占锁 final ReentrantLock lock = this.lock; lock.lock(); try { // 当前代 final Generation g = generation; // 如果这代损坏了,抛出异常 if (g.broken) throw new BrokenBarrierException(); // 如果线程中断了,抛出异常 if (Thread.interrupted()) { // 将损坏状态设置为true // 并通知其他阻塞在此栅栏上的线程 breakBarrier(); throw new InterruptedException(); } // 获取下标 int index = --count; // 如果是 0,说明最后一个线程调用了该方法 if (index == 0) { // tripped boolean ranAction = false; try { final Runnable command = barrierCommand; // 执行栅栏任务 if (command != null) command.run(); ranAction = true; // 更新一代,将count重置,将generation重置 // 唤醒之前等待的线程 nextGeneration(); return 0; } finally { // 如果执行栅栏任务的时候失败了,就将损坏状态设置为true if (!ranAction) breakBarrier(); } } // loop until tripped, broken, interrupted, or timed out for (;;) { try { // 如果没有时间限制,则直接等待,直到被唤醒 if (!timed) trip.await(); // 如果有时间限制,则等待指定时间 else if (nanos > 0L) nanos = trip.awaitNanos(nanos); } catch (InterruptedException ie) { // 当前代没有损坏 if (g == generation && ! g.broken) { // 让栅栏失效 breakBarrier(); throw ie; } else { // 上面条件不满足,说明这个线程不是这代的 // 就不会影响当前这代栅栏的执行,所以,就打个中断标记 Thread.currentThread().interrupt(); } } // 当有任何一个线程中断了,就会调用breakBarrier方法 // 就会唤醒其他的线程,其他线程醒来后,也要抛出异常 if (g.broken) throw new BrokenBarrierException(); // g != generation表示正常换代了,返回当前线程所在栅栏的下标 // 如果 g == generation,说明还没有换代,那为什么会醒了? // 因为一个线程可以使用多个栅栏,当别的栅栏唤醒了这个线程,就会走到这里,所以需要判断是否是当前代。 // 正是因为这个原因,才需要generation来保证正确。 if (g != generation) return index; // 如果有时间限制,且时间小于等于0,销毁栅栏并抛出异常 if (timed && nanos <= 0L) { breakBarrier(); throw new TimeoutException(); } } } finally { // 释放独占锁 lock.unlock(); } }
dowait(boolean, long)方法的主要逻辑处理比较简单,如果该线程不是最后一个调用await方法的线程,则它会一直处于等待状态,除非发生以下情况:
最后一个线程到达,即index == 0
某个参与线程等待超时
某个参与线程被中断
调用了CyclicBarrier的reset()方法。该方法会将屏障重置为初始状态。
CountDownLatch与CyclicBarrier
- CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;CountDownLatch强调一个线程等多个线程完成某件事情。CyclicBarrier是多个线程互等,等大家都完成,再携手共进。
- 调用CountDownLatch的countDown方法后,当前线程并不会阻塞,会继续往下执行;而调用CyclicBarrier的await方法,会阻塞当前线程,直到CyclicBarrier指定的线程全部都到达了指定点的时候,才能继续往下执行;
- CountDownLatch方法比较少,操作比较简单,而CyclicBarrier提供的方法更多,比如能够通过getNumberWaiting(),isBroken()这些方法获取当前多个线程的状态,并且CyclicBarrier的构造方法可以传入barrierAction,指定当所有线程都到达时执行的业务功能;
- CountDownLatch是不能复用的,而CyclicBarrier是可以复用的。
链接:https://juejin.im/post/5aeec3ebf265da0ba76fa327
链接:https://blog.csdn.net/qq_38293564/article/details/80558157