Java 多线程 - CountDownLatch

本文介绍如何利用CountDownLatch类在Java中实现线程之间的同步,通过设置计数器来控制线程执行流程,确保所有操作在指定条件满足后才开始执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

来看一下这个类的构造方法,如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public CountDownLatch(int count) {  
  2.        if (count < 0throw new IllegalArgumentException("count < 0");  
  3.        this.sync = new Sync(count);  
  4.    }  
构造函数需要提供一个count参数,表示一个计数器。此外就是两个用来控制锁存器的方法:countDown()和await()。

首先来看一下await()方法,如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public void await() throws InterruptedException {  
  2.     sync.acquireSharedInterruptibly(1);  
  3. }  
  4.   
  5. public boolean await(long timeout, TimeUnit unit)throws InterruptedException {  
  6.     return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));  
  7. }  
调用了acquireSharedInterruptibly()方法,这个方法的源代码如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public final void acquireSharedInterruptibly(long arg)  throws InterruptedException {  
  2.     if (Thread.interrupted())       // 如果线程被中断,则抛出异常  
  3.         throw new InterruptedException();  
  4.     if (tryAcquireShared(arg) < 0)  // 如果tryAcquireShared()方法获取失败,则调用如下的方法  
  5.         doAcquireSharedInterruptibly(arg);  
  6. }  

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. protected int tryAcquireShared(int acquires) {  
  2.     return (getState() == 0) ? 1 : -1;  
  3. }  
在CountDownLatch类中定义了一个private volatile long类型的state变量,表示锁计数器。通过调用getState()获取这个锁计数器的值。如果为0,则返回1,表示成功,否则表示失败。继续调用如下的方法:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. private void doAcquireSharedInterruptibly(long arg)  
  2.     throws InterruptedException {  
  3.     // 创建"当前线程"的Node节点,且Node中记录的锁是"共享锁"类型;并将该节点添加到CLH队列末尾。  
  4.     final Node node = addWaiter(Node.SHARED);  
  5.     boolean failed = true;  
  6.     try {  
  7.         for (;;) {  
  8.             // 获取上一个节点。  
  9.             // 如果上一节点是CLH队列的表头,则"尝试获取共享锁"。  
  10.             final Node p = node.predecessor();  
  11.             if (p == head) {  
  12.                 long r = tryAcquireShared(arg);  
  13.                 if (r >= 0) {  
  14.                     setHeadAndPropagate(node, r);  
  15.                     p.next = null// help GC  
  16.                     failed = false;  
  17.                     return;  
  18.                 }  
  19.             }  
  20.             // (上一节点不是CLH队列的表头) 当前线程一直等待,直到获取到共享锁。  
  21.             // 如果线程在等待过程中被中断过,则再次中断该线程(还原之前的中断状态)。  
  22.             if (shouldParkAfterFailedAcquire(p, node) &&  
  23.                 parkAndCheckInterrupt())  
  24.                 throw new InterruptedException();  
  25.         }  
  26.     } finally {  
  27.         if (failed)  
  28.             cancelAcquire(node);  
  29.     }  
  30. }  





接下来看一下countDown()方法,如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public void countDown() {  
  2.        sync.releaseShared(1);  
  3. }  
releaseShared()方法在AQS中实现,代码如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public final boolean releaseShared(int arg) {  
  2.        if (tryReleaseShared(arg)) {  
  3.            doReleaseShared();  
  4.            return true;  
  5.        }  
  6.        return false;  
  7. }  
首先调用tryReleaseShared()方法:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. protected boolean tryReleaseShared(int releases) {  
  2.     // Decrement count; signal when transition to zero  
  3.     for (;;) {  
  4.         // 获取锁计数器的状态  
  5.         int c = getState();  
  6.         if (c == 0)  
  7.             return false;  
  8.         // 锁计数器减去1  
  9.         int nextc = c-1;  
  10.         // 通过CAS函数进行赋值  
  11.         if (compareAndSetState(c, nextc))  
  12.             return nextc == 0;  
  13.     }  
  14. }  

举个例子:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public class CountDownLatchTest1 {  
  2.   
  3.     private static int LATCH_SIZE = 5;  
  4.     private static CountDownLatch doneSignal;  
  5.     public static void main(String[] args) {  
  6.   
  7.         try {  
  8.             doneSignal = new CountDownLatch(LATCH_SIZE);  
  9.   
  10.             // 新建5个任务  
  11.             for(int i=0; i<LATCH_SIZE; i++)  
  12.                 new InnerThread().start();  
  13.   
  14.             System.out.println("main await begin.");  
  15.             // "主线程等待线程池中5个任务的完成  
  16.             doneSignal.await();  
  17.   
  18.             System.out.println("main await finished.");  
  19.         } catch (InterruptedException e) {  
  20.             e.printStackTrace();  
  21.         }  
  22.     }  
  23.   
  24.     static class InnerThread extends Thread{  
  25.         public void run() {  
  26.             try {  
  27.                 Thread.sleep(1000);  
  28.                 System.out.println(Thread.currentThread().getName() + " sleep 1000ms.");  
  29.                 // 将CountDownLatch的数值减1  
  30.                 doneSignal.countDown();  
  31.             } catch (InterruptedException e) {  
  32.                 e.printStackTrace();  
  33.             }  
  34.         }  
  35.     }  
  36. }  
运行的结果如下:

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. main await begin.  
  2. Thread-0 sleep 1000ms.  
  3. Thread-2 sleep 1000ms.  
  4. Thread-1 sleep 1000ms.  
  5. Thread-4 sleep 1000ms.  
  6. Thread-3 sleep 1000ms.  
  7. main await finished.  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值