多线程之CountDownLatch

CountDownLatch

1、概述

countdownlatch,对应的中文意思是倒数计时栅栏。对应着java中的线程,就是多个线程会同时来到栅栏,等待栅栏打开。

这个更适用于我们平常测试多线程条件下,模拟搞并发场景,多个请求同时发起请求来进行模拟业务逻辑是否还能够执行成功。

底层基于 AbstractQueuedSynchronizer 实现,CountDownLatch 构造函数中指定的count直接赋给AQS的state;

具体的执行流程如下所示:

  • 每次countDown()则都是release(1)减1,最后减到0时unpark阻塞线程;这一步是由最后一个执行countdown方法的线程执行的;

  • 而调用await()方法时,当前线程就会判断state属性是否为0,如果为0,则继续往下执行,如果不为0,则使当前线程进入等待状态,直到某个线程将state属性置为0,其就会唤醒在await()方法中等待的线程。;

2、常用方法介绍

方法名称 描述
await() 线程挂起,直到count=o才会继续执行
boolean await(long timeout, TimeUnit unit) 等待time时间后,count的值还不是0,不再等待,那么将继续执行
countDown() 会将count的值-1,直到为0

3、案例分析

/**
 * @author lg
 * @Description 使用CountDownLatch
 * @date 2022/10/17 11:33
 */
public class CountDownLatchOne {

    private static Logger logger = LoggerFactory.getLogger(CountDownLatchOne.class);

    static CountDownLatch countDownLatch = new CountDownLatch(5);

    public static void main(String[] args) {
        logger.info("main start");
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                logger.info("start");
                logger.info("当前线程名称是:{}",Thread.currentThread().getName());
                logger.info("end");
                countDownLatch.countDown();
            },"t"+(i+1)).start();
        }
        try {
            // 阻塞队列中进行等待
            countDownLatch.await();
            logger.info("main end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3.1、从构造方法中分析

要求count必须要大于0;

3.2、countDown方法

每个线程循环遍历,根据状态来抢锁。如果抢到了,那么利用CAS来进行操作。

这里需要注意到一个问题:当state为0的时候,再次来抢锁的时候,那么就不应该抢到。所以这里做了一个判断:

if (c == 0) return false;

3.3、await方法

如果线程被中断了,那么这个时候还来进行操作,将会造成抛出异常的错误信息。

否则将会来尝试获取得到锁。

尝试获取得到锁的流程也很简单。判断当前state状态是否为0。

那么要想进入到阻塞状态的时候,就需要state不为0的时候

那么看下阻塞方法

这里添加等待者和之前是不同的,这里创建的结点为Node.SHARED。

唤醒方法

当最后一个线程将state变为0的时候,就会通知阻塞的线程

6、CountDownLatch和Thread.join()方法的区别

  • 1、CountDownLatch的作用就是允许一个或多个线程等待其他线程完成操作,看起来有点类似join() 方法,但其提供了比 join() 更加灵活的API。
  • 2、CountDownLatch可以手动控制在n个线程里调用n次countDown()方法使计数器进行减一操作,也可以在一个线程里调用n次执行减一操作。 而 join() 的实现原理是不停检查join线程是否存活,如果 join 线程存活则让当前线程永远等待。所以两者之间相对来说还是CountDownLatch使用起来较为灵活。
posted @ 2022-03-20 21:32  雩娄的木子  阅读(514)  评论(0编辑  收藏  举报