一、是什么
顾名思义,Latch是门闩的意思,把到达门闩的线程都阻塞住,CountDown是减少计数的意思。
所以CountDownLatch是当每个线程到达某个状态就将计数减一,计数为0时所有被阻塞线程全部被唤醒。
二、内部实现
CountDownLatch是基于AQS实现的。
和Semaphore不同的是,CountDownLatch只有一个内部实现类Sync。所以没有公平锁和非公平锁之分。
三、常用方法的底层实现
最重要的两个方法,countDown()和await()。
countDown()
目的:让计数器减一,直到计数器值为0,调用await()方法的线程(已被阻塞)则被唤醒,继续执行。
public void countDown() {
sync.releaseShared(1);
}
countDown()方法是使用AQS的releaseShared()方法,详见AQS。
await()
目的: 调用了await()方法的线程会判断计数器是否为0,若不为0则该线程进入阻塞队列,否则该线程不被阻塞。另外,进入到阻塞队列当中的线程会在计数器减为0时被唤醒。
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
可以看到await方法内部实际是调用AQS的doAcquireSharedInterruptibly()方法。
综上可见,CountDownLatch就是AQS的一个使用场景。
四、常用场景实例
梨子1:主线程等待子线程执行完毕
package com.example.demo.CountDownLatchDemo;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 主线程等待子线程执行完成再执行
*/
public class CountdownLatchTest1 {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(3);
final CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println("子线程" + Thread.currentThread().getName() + "开始执行");
Thread.sleep((long) (Math.random() * 10000));
System.out.println("子线程"+Thread.currentThread().getName()+"执行完成");
latch.countDown();//当前线程调用此方法,则计数减一
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try {
System.out.println("主线程"+Thread.currentThread().getName()+"等待子线程执行完成...");
latch.await();//阻塞当前线程,直到计数器的值为0
System.out.println("主线程"+Thread.currentThread().getName()+"开始执行...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
栗子2:
百米赛跑,4名运动员选手到达场地等待裁判口令,裁判一声口令,选手听到后同时起跑,当所有选手到达终点,裁判进行汇总排名
package com.example.demo.CountDownLatchDemo;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountdownLatchTest2 {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CountDownLatch cdOrder = new CountDownLatch(1);
final CountDownLatch cdAnswer = new CountDownLatch(4);
for (int i = 0; i < 4; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println("选手" + Thread.currentThread().getName() + "正在等待裁判发布口令");
cdOrder.await();
System.out.println("选手" + Thread.currentThread().getName() + "已接受裁判口令");
Thread.sleep((long) (Math.random() * 10000));
System.out.println("选手" + Thread.currentThread().getName() + "到达终点");
cdAnswer.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try {
Thread.sleep((long) (Math.random() * 10000));
System.out.println("裁判"+Thread.currentThread().getName()+"即将发布口令");
cdOrder.countDown();
System.out.println("裁判"+Thread.currentThread().getName()+"已发送口令,正在等待所有选手到达终点");
cdAnswer.await();
System.out.println("所有选手都到达终点");
System.out.println("裁判"+Thread.currentThread().getName()+"汇总成绩排名");
} catch (InterruptedException e) {
e.printStackTrace();
}
service.shutdown();
}
}
参考文章:
https://www.cnblogs.com/Lee_xy_z/p/10470181.html