CountDownLatch
Semaphore
更加适合用于控制对有限资源的访问,特别是当你需要允许一定数量的线程同时访问资源时
CountDownLatch
更加适合用于协调多个线程的完成状态,确保在某些操作完成后再执行后续操作
它用于协调多个线程的执行,使得某些操作必须等到其他操作完成后才能继续进行,常用方法如下
CountDownLatch latch = new CountDownLatch(3); // 创建一个计数器,计数器的值为 3(需要等待的线程数量或事件数量是 3)
latch.countDown(); // 减少计数器的值,每次调用都会将计数器 -1
latch.await(); // 在哪个线程调用 await,哪个线程就会阻塞,直到计数器为 0 才恢复运行
用法示例
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
// 创建一个 CountDownLatch,初始计数为3
CountDownLatch latch = new CountDownLatch(3);
// 启动3个线程,每个线程完成后调用countDown()
for (int i = 0; i < 3; i++) {
new Thread(new MyThread(latch)).start();
}
// 主线程等待,直到计数器减到零
latch.await();
System.out.println("All workers have finished their tasks.");
}
}
class MyThread implements Runnable {
private CountDownLatch latch;
public Worker(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
try {
// 模拟任务处理
Thread.sleep(1000);
System.out.println("Worker finished");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 完成任务后调用 countDown
latch.countDown();
}
}
}
原理
- state 初始设置了值,当主线程调用 await() 发现 state 不是 0,获取不到锁,主线程就进入队列阻塞
- 每调用一次 countDown() 方法,state -1,当减后是0,需要唤醒等待的线程
- 需要注意的是,当 state 已经是 0,调用 countDown() 方法是无意义的
构建一个计数器
// 构造方法
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
// new 了一个 Sync,和前端的所有锁都是一样的,这个 Sync 继承了 AQS
this.sync = new Sync(count);
}
// Sync 构造方法
Sync(int count) {
setState(count); // 调用父类 AQS 的方法,修改 state 的值
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer#setState
protected final void setState(int newState) {
state = newState;
}
计数器-1
// java.util.concurrent.CountDownLatch#countDown
public void countDown() {
sync.releaseShared(1); // AQS 的方法
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer#releaseShared
// 1,返回值没意义,因为在 countDown 方法中没根据返回值做什么处理
// 2,tryReleaseShared() 方法返回 true:说明是 state - 1 后为 0,需要唤醒等待线程
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) { // 模板方法
doReleaseShared(); // 唤醒等待线程
return true;
}
return false;
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer#tryReleaseShared
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
// java.util.concurrent.CountDownLatch.Sync#tryReleaseShared
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0) // 已经是 0,返回 false
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0; // 仅当 state - 1 后 state 是 0 才返回 true
}
}
await() 方法
这个就更简单了
// java.util.concurrent.CountDownLatch#await()
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireSharedInterruptibly
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0) // 获取锁,只会返回 1 或 -1(-1 表示不能获取锁,要阻塞)
doAcquireSharedInterruptibly(arg); // 前面分析过好多次了,进入队列,维护链表,挂起线程
}
// java.util.concurrent.CountDownLatch.Sync#tryAcquireShared
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1; // 当 state 不是 0,返回 -1
}
标签:int,state,util,计数器,线程,CountDownLatch,latch
From: https://www.cnblogs.com/cyrushuang/p/18404620