乐趣区

关于java:CountDownLatchCyclicBarrierSemaphore的使用及底层实现

明天咱们就具体介绍一下 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…

退出移动版