多线程之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使用起来较为灵活。
从理论中来,到实践中去,最终回归理论