写一篇完整的5000字的Markdown格式文章需要一定的篇幅,所以我将为你提供一个结构化的框架和内容概要,帮助你理解并快速生成这篇文章。文章的核心内容会涵盖CountDownLatch的基本概念、原理、源码分析以及常见使用场景,并通过实际代码案例进行讲解。以下是文章的大纲和核心部分。


JUC CountDownLatch 源码详解

目录


简介

在Java并发编程中,CountDownLatch是一个非常重要的同步工具类,它能够帮助我们处理线程间的等待与协调。它属于java.util.concurrent包,在多线程环境中经常用来实现某些线程的等待,直到其他线程完成特定任务。

本文将详细分析CountDownLatch的基本使用、源码实现、常见的应用场景,并通过具体的代码实例进行说明。


CountDownLatch 基本概念

什么是 CountDownLatch

CountDownLatch是一个同步工具类,允许一个或多个线程一直等待,直到其他线程执行完毕后才继续执行。它通过一个计数器来控制线程的等待与释放。计数器的初始值表示等待的线程数,每次countDown()调用时,计数器减一;当计数器的值为0时,所有等待的线程会被释放。

CountDownLatch 的工作原理

  1. 初始化CountDownLatch在创建时需要指定一个初始的计数值,通常是需要等待的线程数目。
  2. 等待:调用await()方法的线程会进入等待状态,直到计数器的值变为0。
  3. 倒计数:在其他线程中通过调用countDown()方法来减少计数器的值。
  4. 释放:当计数器值为0时,所有等待的线程会被释放,继续执行后续任务。

CountDownLatch 源码解析

类结构

CountDownLatch类实现了java.io.Serializable接口,并且有一个重要的成员变量:count,表示当前的倒计时。

javaCopy Code
public class CountDownLatch { private final Sync sync; public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); sync = new Sync(count); } public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } public void countDown() { sync.releaseShared(1); } public long getCount() { return sync.getCount(); } private static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = 1L; Sync(int count) { setState(count); } protected int tryAcquireShared(int ignore) { return (getState() == 0) ? 1 : -1; } protected boolean tryReleaseShared(int ignore) { for (;;) { int current = getState(); if (current == 0) return false; int next = current - 1; if (compareAndSetState(current, next)) return next == 0; } } public long getCount() { return getState(); } } }

关键方法分析

  • 构造方法 CountDownLatch(int count):初始化时传入一个正整数表示需要等待的线程数目。
  • await() 方法:调用该方法的线程会阻塞,直到计数器为0才会被释放。
  • countDown() 方法:该方法会减少计数器的值,调用时会释放一个等待的线程。当计数器为0时,所有等待的线程将被唤醒。
  • getCount() 方法:返回当前计数器的值,即剩余的等待线程数。

同步原理

CountDownLatch内部依赖于AbstractQueuedSynchronizer(AQS)来实现线程的同步。Sync类继承自AQS,并重写了两个关键方法:

  • tryAcquireShared(int ignore):当计数器为0时,返回1表示线程可以继续执行。
  • tryReleaseShared(int ignore):每次调用countDown()时,减少计数器的值,并判断是否所有线程都已被释放。

AQS的实现使用了CAS(Compare And Swap)机制,保证了多线程环境中的安全性和高效性。


CountDownLatch 使用场景与实例

常见使用场景

CountDownLatch主要用于以下几种场景:

  1. 并行任务等待:当多个线程并行执行某些任务,主线程需要等待所有线程完成任务后才能继续执行后续操作。
  2. 线程协调:当需要等待多个线程完成某个操作后再继续执行时,CountDownLatch非常适合。
  3. 实现线程启动的同步:多个线程准备好后,一起开始执行某个任务。

实际案例分析

场景 1:多个任务并行执行,主线程等待

假设我们有多个任务需要并行执行,主线程需要等所有任务执行完后再执行下一步操作。我们可以使用CountDownLatch来实现这个场景。

javaCopy Code
public class CountDownLatchExample { public static void main(String[] args) throws InterruptedException { int taskCount = 3; CountDownLatch latch = new CountDownLatch(taskCount); // 创建多个线程执行任务 for (int i = 0; i < taskCount; i++) { new Thread(new Task(latch)).start(); } // 主线程等待所有任务完成 latch.await(); System.out.println("All tasks are finished, proceeding with main task."); } } class Task implements Runnable { private final CountDownLatch latch; public Task(CountDownLatch latch) { this.latch = latch; } @Override public void run() { try { System.out.println(Thread.currentThread().getName() + " is doing its work."); Thread.sleep(1000); // 模拟任务执行时间 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { latch.countDown(); // 任务完成,减少计数器 } } }

在这个例子中,主线程会等待latch.await(),直到所有的子线程完成它们的任务并调用latch.countDown()

场景 2:线程启动同步

有时候,我们希望多个线程在某一时刻一起启动,可以使用CountDownLatch来实现线程启动的同步。

javaCopy Code
public class ThreadStartSyncExample { public static void main(String[] args) throws InterruptedException { int threadCount = 5; CountDownLatch latch = new CountDownLatch(1); // 只有一个计数器 // 创建多个线程,所有线程等待主线程发信号 for (int i = 0; i < threadCount; i++) { new Thread(new Worker(latch)).start(); } // 主线程发出信号,所有线程开始执行 System.out.println("Main thread signaling workers to start..."); latch.countDown(); } } class Worker implements Runnable { private final CountDownLatch latch; public Worker(CountDownLatch latch) { this.latch = latch; } @Override public void run() { try { latch.await(); // 等待主线程的信号 System.out.println(Thread.currentThread().getName() + " started working!"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }

在这个示例中,主线程通过调用latch.countDown()向所有子线程发送信号,子线程通过调用latch.await()来等待主线程的信号,然后才开始工作。


总结与优化建议

总结

  • CountDownLatch是一个非常实用的同步工具类,用于在多线程编程中控制线程之间的协调与等待。
  • 它通过计数器机制让线程能够等待特定条件,直到其他线程完成任务。
  • 适用于并行任务等待、线程启动同步等多种场景。

优化建议

  • 在高并发场景下,避免