CountDownLatch是什么?CountDownLatch的作用?
CountDownLatch适用于什么场景?
1. CountDownLatch介绍
CountDownLatch
是 Java 中的一个同步工具类,它允许一个或多个线程等待其他线程完成操作。CountDownLatch
的主要思想是,一个线程等待其他线程完成一组操作,它在倒计时计数器的基础上工作,计数器的初始值是一个正整数,每当一个线程完成一项操作,计数器的值减一。当计数器的值变为零时,等待的线程被释放,可以继续执行。
2. CountDownLatch源码解析
public class CountDownLatch {
// 构造方法
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
// 用于阻塞当前线程,直到计数器的值变为零
public void await() throws InterruptedException {
// 在 Sync 类中实现,用于尝试获取共享许可,如果计数器为零,则直接返回,否则会进入等待队列等待。
sync.acquireSharedInterruptibly(1);
}
// 用于等待一段时间,超时后返回
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
// 用于尝试在规定的时间内获取共享许可,如果在超时前获取到许可则返回 true,否则返回 false。
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
// 用于将计数器的值减一,表示有一个线程完成了一项操作
public void countDown() {
// 在 Sync 类中实现,用于释放共享许可
sync.releaseShared(1);
}
// 用于获取当前计数器的值
public long getCount() {
// 返回当前的计数值
return sync.getCount();
}
}
Sync类
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
// 获取当前计数值的方法
int getCount() {
return getState();
}
// 尝试获取共享许可的方法
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
// 尝试释放共享许可的方法
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
3. CountDownLatch应用场景
- 多线程任务协同:
CountDownLatch
可以用于多个线程协同完成某项任务。一个主线程等待其他多个线程完成任务,每个子线程完成一部分工作后调用countDown()
方法减少计数值,主线程通过await()
方法等待计数值变为零。 - 并行任务的性能测试: 可以用
CountDownLatch
来测量并行任务的性能,主线程可以等待多个并行任务同时开始执行。 - 等待多个服务初始化完成: 在系统启动时,有时需要等待多个服务初始化完成后再继续执行。每个服务初始化完成后调用
countDown()
。 - 分布式系统中的协同: 在分布式系统中,
CountDownLatch
可以用于等待多个节点的某个事件的发生,以协同分布式系统的操作。
4. CountDownLatch示例
模拟拼团活动
public class CountDownLatchDemo {
public static void main(String[] args) {
pt();
}
private static void pt() {
// 模拟拼团活动
final CountDownLatch countDownLatch = new CountDownLatch(10);
List<String> ids = new ArrayList<>();
// 目前有10个人进行拼团
for (int i = 0; i < 30; i++) {
new Thread(() -> {
if (countDownLatch.getCount() > 0) {
synchronized (ids) {
if (countDownLatch.getCount() > 0) {
ids.add(Thread.currentThread().getName());
System.out.println(Thread.currentThread().getName() + "拼团成功!!!");
countDownLatch.countDown();
}
}
}
System.out.println(Thread.currentThread().getName() + "请求拼团!!!商品剩余" + countDownLatch.getCount());
}, String.valueOf(i + 1)).start();
}
new Thread(() -> {
try {
countDownLatch.await();
System.out.println("拼团结束============================================================");
System.out.println("拼团人员id"+ids);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "拼团").start();
}
}
Connected to the target VM, address: '127.0.0.1:54442', transport: 'socket'
1拼团成功!!!
1请求拼团!!!商品剩余9
4拼团成功!!!
4请求拼团!!!商品剩余8
8拼团成功!!!
8请求拼团!!!商品剩余7
6拼团成功!!!
14拼团成功!!!
6请求拼团!!!商品剩余6
5拼团成功!!!
5请求拼团!!!商品剩余4
14请求拼团!!!商品剩余5
7拼团成功!!!
7请求拼团!!!商品剩余3
3拼团成功!!!
3请求拼团!!!商品剩余2
2拼团成功!!!
22拼团成功!!!
2请求拼团!!!商品剩余1
19请求拼团!!!商品剩余0
24请求拼团!!!商品剩余0
13请求拼团!!!商品剩余0
25请求拼团!!!商品剩余0
9请求拼团!!!商品剩余0
20请求拼团!!!商品剩余0
28请求拼团!!!商品剩余0
22请求拼团!!!商品剩余0
30请求拼团!!!商品剩余0
21请求拼团!!!商品剩余0
27请求拼团!!!商品剩余0
17请求拼团!!!商品剩余0
11请求拼团!!!商品剩余0
10请求拼团!!!商品剩余0
12请求拼团!!!商品剩余0
15请求拼团!!!商品剩余0
23请求拼团!!!商品剩余0
16请求拼团!!!商品剩余0
18请求拼团!!!商品剩余0
拼团结束============================================================
26请求拼团!!!商品剩余0
29请求拼团!!!商品剩余0
拼团人员id[1, 4, 8, 6, 14, 5, 7, 3, 2, 22]
Disconnected from the target VM, address: '127.0.0.1:54442', transport: 'socket'