在多线程编程中,经常需要实现一种机制来协调多个线程的执行,以确保某些操作在所有线程完成后再进行。CountDownLatch 就是 Java 并发包中提供的一种同步工具,它能够让一个或多个线程等待其他线程完成操作。
CountDownLatch 是Java 1.5版本推出的一个同步辅助类,在构造时需要指定一个计数值,该计数值表示需要等待的事件数量。每当一个事件完成时,计数值就会减一,当计数值减至零时,等待的线程就会被唤醒继续执行。
CountDownLatch 可以被广泛应用于各种多线程协作的场景,例如:
让我们通过一个示例代码来理解 CountDownLatch 的使用。假设有一个任务需要被分配给多个子线程来完成,并且主线程需要等待所有子线程执行完毕后才能继续执行。
//任务分割的线程数
private static final int THREAD_TOTAL = 10;
//子线程执行的超时时间
private static final int countDownLatchTimeout = 5;
public static void main(String[] args) {
//创建CountDownLatch并设置计数值,该count值可以根据线程数的需要设置
CountDownLatch countDownLatch = new CountDownLatch(THREAD_TOTAL);
//创建线程池,开启、创建异步线程执行任务
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < THREAD_TOTAL; i++) {
cachedThreadPool.execute(() -> {
try {
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + " do something!");
} catch (Exception e) {
System.out.println("Exception: do something exception");
} finally {
//该线程执行完毕-1
countDownLatch.countDown();
}
});
}
//回到主线程中
System.out.println("Back main thread do something");
try {
//主线程等待线程池中完成(子线程执行超时时间)
boolean await = countDownLatch.await(countDownLatchTimeout, TimeUnit.MINUTES);
System.out.println(await);
} catch (InterruptedException e) {
System.out.println("Exception: await interrupted exception");
} finally {
System.out.println("countDownLatch: " + countDownLatch);
}
System.out.println("main thread do something-2");
}
如果您学有余力或手头没有着急的需求,请继续往下看,让我们简单从源码层面分析下CountDownLatch的实现。
我截取了CountDownLatch
内部关键实现逻辑来分析其实现原理:
CountDownLatch
的功能主要通过内部类Sync
实现,在内部类中,Sync
继承自AbstractQueuedSynchronizer
来实现同步操作,AbstractQueuedSynchronizer
提供了同步器实现的基础框架,通过该类,开发者可以相对容易地实现自定义的同步器,例如独占锁、共享锁、信号量等。
/**
* Synchronization control For CountDownLatch.
* Uses AQS state to represent count.
*/
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;
}
}
}
private final Sync sync;
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(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;
}
}
tryReleaseShared 方法尝试释放共享资源,首先通过一个无限循环不断尝试,在循环中获取当前状态值,如果状态值已经为0,则直接返回false;否则将状态值减1,并尝试原子性地设置状态值,如果设置成功,则返回是否状态值变为0,否则继续循环。
总的来说,这段代码实现了一个简单的 CountDownLatch 功能,通过 tryAcquireShared 方法尝试获取共享资源,通过 tryReleaseShared 方法尝试释放共享资源。当共享资源的状态值为0时,表示所有等待的线程都已被释放。
在JDK 1.8后,java.util.concurrent
包提供了CompletableFuture
类用于支持异步编程和异步任务的处理,相较于CountDownLatch
,它提供了更丰富的API,就个人而言,我更喜欢CompletableFuture
,因为它扩展性强,更适合JDK 8提供的函数式编程特性,代码更加优雅。
CountDownLatch 和 CompletableFuture 都是 Java 中用于多线程协作的工具,它们各自适用于不同的场景。CountDownLatch 更适合简单的多线程协作,而 CompletableFuture 则更适合复杂的异步编程场景。在实际应用中,我们可以根据具体的需求选择合适的工具来实现多线程协作和异步编程,以达到更好的开发效率和代码质量。
关于我
你好,我是Debug.c。微信公众号:种颗代码技术树 的维护者,一个跨专业自学Java,对技术保持热爱的bug猿,同样也是在某二线城市打拼四年余的Java Coder。
在掘金、CSDN、公众号我将分享我最近学习的内容、踩过的坑以及自己对技术的理解。
如果您对我感兴趣,请联系我。
若有收获,就点个赞吧