明天咱们就具体介绍一下JUC的一些罕用同步工具类,缩小计数(CountDownLatch),循环栅栏(CyclicBarrier),信号灯(Semaphore)的应用和区别。
CountDownLatch
作用:就是一个或者多个线程在开始执行操作之前,必须要等到其余线程执行实现才能够执行。
类的构造方法CountDownLatch(int count)
结构一个用给定计数初始化的值。
public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); }
外围办法
countDown()
递加锁存器的计数,如果计数达到零,将开释所有期待的线程
public void countDown() { sync.releaseShared(1);}
await()
使以后线程在锁存器倒计数至零之前始终处于期待。
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1);}
当线程调用await()办法时,就会阻塞以后线程。当线程调用一次countDown()办法时,count就会减一,直到当count 的值等于0时候,被阻塞的线程才能够继续执行。
当初咱们用一个生存中的例子阐明:学生时代,当咱们在考试的时候,监考老师必须等到所有的学生交完卷子才能够来到,此时监考老师就相当于期待线程,而学生就好比是执行的线程。
咱们用代码实现这个案例:
参加考试的学生10个, main线程就相当于监考老师
public class CountDownLatchDemo { private static CountDownLatch countDownLatch = new CountDownLatch(10); public static void main(String[] args) throws InterruptedException { for (int i = 1; i <= 10; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"\t 学生交卷:"); // 减一 countDownLatch.countDown(); } },String.valueOf(i)).start(); } // 期待 countDownLatch.await(); System.out.println(Thread.currentThread().getName()+"\t 监考老师来到教室"); }}
底层实现原理
从源码咱们不难发现CountDownLatch是基于AQS实现的,当咱们在构建CountDownLatch对象时,传入的值其实就会赋值给 AQS 的要害变量state,执行countDown()办法时,其实就是利用CAS 将state 减一,执行await()办法时,其实就是判断state是否为0,不为0则退出到队列中,将该线程阻塞掉(除了头节点),因为头节点会始终自旋期待state为0,当state为0时,头节点把残余的在队列中阻塞的节点也一并唤醒。
CyclicBarrier
作用:N个线程互相期待,任何一个线程实现之前,所有的线程都必须期待。
罕用的构造方法有:CyclicBarrier(int parties,Runnable barrierAction)
创立一个新的CyclicBarrier,它将在给定数量的线程处于期待状态时启动,并在启动barrier时执行给定的屏障操作,该操作由最初一个进入barrier的线程操作
public CyclicBarrier(int parties, Runnable barrierAction) { if (parties <= 0) throw new IllegalArgumentException(); this.parties = parties; this.count = parties; this.barrierCommand = barrierAction;}
外围办法
await()
在所有的参与者都曾经在此barrier上调用await办法之前始终期待
public int await() throws InterruptedException, BrokenBarrierException { try { return dowait(false, 0L); } catch (TimeoutException toe) { throw new Error(toe); // cannot happen } }
生存中的例子:在打王者的时候,在开局前所有人都必须要加载到100%才能够进入。否则所有玩家都互相期待。
public class CyclicBarrierDemo { public static void main(String[] args) { CyclicBarrier cyclicBarrier =new CyclicBarrier(5, () -> System.out.println("游戏开始")); for (int i = 1; i <=5 ; i++) { int finalI = i; new Thread(new Runnable() { @Override public void run() { System.out.println("第"+ finalI +"进入游戏"); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } },String.valueOf(i)).start(); } }}
总结:CyclicBarrier 的构造方法第一个参数是指标阻碍数,每次执行 CyclicBarrier 一次阻碍数会加一,如果达到了指标阻碍数,才会执行 await()之后办法。
底层实现原理
从源码不难发现的是,它没有像CountDownLatch和ReentrantLock应用AQS的state变量,而CyclicBarrier是间接借助ReentrantLock加上Condition 期待唤醒的性能 进而实现的。
在构建CyclicBarrier时,传入的值会赋值给CyclicBarrier外部保护count变量,也会赋值给parties变量,每次调用await()办法时,会将count 减一 ,操作count值是间接应用ReentrantLock来保障线程安全性。如果count不为0,则增加Condition队列中,如果count等于0时,则把节点从Condition队列增加至AQS的队列中进行全副唤醒,并且将parties的值从新赋值为count的值。
Semaphore
信号量,用来管制同一时间,资源可被拜访的线程数量,个别利用场景流量的管制。
构造方法Semaphore(int permits)
创立具备给定的许可数和非偏心的偏心设置的Semapore
public Semaphore(int permits) { sync = new NonfairSync(permits);}
外围办法acquire()
从此信号量获取一个许可,在提供一个许可前始终将线程阻塞,否则线程被中断,release()
开释一个许可,将其返回给信号量,设置许可数量Semaphore semaphore = new Semaphore(3)
,个别acquire()
都会抛出异样,release
在finally
中执行。
public void acquire() throws InterruptedException { sync.acquireSharedInterruptibly(1);}
public void release() { sync.releaseShared(1);}
举例说明,6辆强站三个停车位。
public class SemaphoreDemo { public static void main(String[] args) { // 模仿资源类,有3个空车位 Semaphore semaphore = new Semaphore(3); for (int i = 1; i <=6 ; i++) { new Thread(() -> { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"\t 抢到了车位"); TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }finally { semaphore.release(); } System.out.println(Thread.currentThread().getName()+"\t 来到了车位"); semaphore.release(); },String.valueOf(i)).start(); } }}
Semaphore 底层实现是基于AQS实现的相似于CountDownLatch
总结
CountDownLatch,Semaphore都是基于AQS实现。
CountDownLatch是一个线程期待其余线程,CyclicBarrier是线程之间互相期待。
CountDownLatch会将结构CountDownLatch的入参传递至state,countDown()就是在利用CAS将state减一,await()理论就是让头节点始终在期待state为0时,开释所有期待的线程。
CyclicBarrier则利用ReentrantLock和Condition,本身保护了count和parties变量。每次调用await将count减一,并将线程退出到Condition队列上。等到count为0时,则将Condition队列的节点移交至AQS队列,并全副开释。
参考资料:
https://javainterview.gitee.i...
https://blog.csdn.net/weixin_...
https://mp.weixin.qq.com/s/TD...