明天咱们就具体介绍一下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()之后办法。

底层实现原理

从源码不难发现的是,它没有像CountDownLatchReentrantLock应用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()都会抛出异样,releasefinally中执行。

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则利用ReentrantLockCondition,本身保护了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...