Java基础 -> CountDownLatch丶CyclicBarraer和Semaphore的区别和底层原理

CountDownLatch丶CyclicBarraer和Semaphore的区别和底层原理

CountDownLatch表示计数器,可以给CountDownLatch设置⼀个数字,CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信

  • 从名字可以猜出,是做数量减法的一种机制

  • CountDownLatch能够使线程等待另外一些线程完成各自工作之后,再继续执行。

    • 另外的线程每执行完一个计数器就减一
  • 调⽤await()⽅法阻塞线程,是利⽤AQS排队,加入阻塞队列

  • 其他线程可以调⽤CountDownLatch的countDown()⽅法来对 CountDownLatch(计数器)中的数字减⼀

  • 当数字被减成0后,所有await阻塞的线程都将被唤醒。

    • 例如一些核心组件线程和我们的工作线程
    • 我们可以把我们的工作线程用**await()**方法加入到阻塞队列中
    • 然后先执行依赖的核心组件线程,每执行完成一个计数器减一
    • 直到减完了,我们的工作线程就会被队列以先进先出的方式全部唤醒,正常执行
//定义一个计数器,计数器参数表示在计数器阻塞线程执行之前该有多少线程执行
CountDownLatch countDownLatch = new CountDownLatch(2);
//第一个线程
new Thread(() -> {
    //计数器减一,如果计数器为0,则释放所有阻塞线程
    countDownLatch.countDown();
}).start();
//第二个线程
new Thread(() -> {
 	//计数器减一,如果计数器为0,则释放所有阻塞线程
    countDownLatch.countDown();
}).start();
//当前线程加入阻塞队列
countDownLatch.await();

相对的还有一个CyclicBarraer

  • CyclicBarraer可以使一组线程等待,当线程数量够时,一起执行
  • 利用await()阻塞,每次阻塞一个线程并且计数变量减一
  • 当计数变量为0时,将之前阻塞的线程都一起唤醒
    • 当阻塞的线程达到了规定的数量的时候就一起释放
CyclicBarrier barrier = new CyclicBarrier(3, () -> {...});
for (int i = 0; i < 3; i++) {
    new Thread(() -> {
 	//将此线程阻塞并计数器减一,如果计数器为0,则释放所有刚刚阻塞线程
    barrier.await();
	}).start();
}

CountDownLatch是先执行前置的线程,再执行任务线程

CyclicBarraer是指定一定数量的线程在某一刻一起执行

Semaphore表示信号量,Semaphore可以控制同时访问的线程个数

  • 通过acquire()来获取许可,如果没有许可可⽤则线程阻塞,并通过AQS来排队
    • 许可就是判断同时访问的线程有没有超过指定的限制
    • 如果同时访问的线程过多,则将申请的线程加入阻塞队列
    • 这是一种共享锁,AQS的state变量记录的是共享锁的数量
    • 不再是独占锁那样,记录的是0还是大于0去判断是否上锁
  • 可以通过release()⽅法来释放许可,当某个线程执行完释放了某个许可后,会从AQS中正在排队的第⼀个线程开始依次唤醒,直到没有空闲许可
    • 释放许可就是表示可以使多余的线程执行
//同时访问的线程个数 state=2
private static Semaphore semaphore = new Semaphore(2);
for (int i = 0; i < 5; i++) {
    new Thread(() -> {
        try {
            // 获取令牌尝试进入
            semaphore.acquire();
            // 释放令牌
            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
}

版权声明:本文为rod0320原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。