欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解Java多线程编程中CountDownLatch阻塞线程的方法

程序员文章站 2024-03-13 13:10:21
直译过来就是倒计数(countdown)门闩(latch)。倒计数不用说,门闩的意思顾名思义就是阻止前进。在这里就是指 countdownlatch.await() 方法在...

直译过来就是倒计数(countdown)门闩(latch)。倒计数不用说,门闩的意思顾名思义就是阻止前进。在这里就是指 countdownlatch.await() 方法在倒计数为0之前会阻塞当前线程。
countdownlatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
countdownlatch 的作用和 thread.join() 方法类似,可用于一组线程和另外一组线程的协作。例如,主线程在做一项工作之前需要一系列的准备工作,只有这些准备工作都完成,主线程才能继续它的工作。这些准备工作彼此独立,所以可以并发执行以提高速度。在这个场景下就可以使用 countdownlatch 协调线程之间的调度了。在直接创建线程的年代(java 5.0 之前),我们可以使用 thread.join()。在 juc 出现后,因为线程池中的线程不能直接被引用,所以就必须使用 countdownlatch 了。
countdownlatch类是一个同步计数器,构造时传入int参数,该参数就是计数器的初始值,每调用一次countdown()方法,计数器减1,计数器大于0 时,await()方法会阻塞程序继续执行。 countdownlatch可以看作是一个倒计数的锁存器,当计数减至0时触发特定的事件。利用这种特性,可以让主线程等待子线程的结束。 下面以一个模拟运动员比赛的例子加以说明。
countdownlatch的一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。假如我们这个想要继续往下执行的任务调用一个countdownlatch对象的await()方法,其他的任务执行完自己的任务后调用同一个countdownlatch对象上的countdown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个countdownlatch对象的计数值减到0为止。

countdownlatch函数列表

countdownlatch(int count)
构造一个用给定计数初始化的 countdownlatch。
// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
void await()
// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。
boolean await(long timeout, timeunit unit)
// 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
void countdown()
// 返回当前计数。
long getcount()
// 返回标识此锁存器及其状态的字符串。
string tostring()

countdownlatch数据结构
countdownlatch的uml类图如下:

详解Java多线程编程中CountDownLatch阻塞线程的方法

countdownlatch的数据结构很简单,它是通过"共享锁"实现的。它包含了sync对象,sync是sync类型。sync是实例类,它继承于aqs。


countdownlatch的使用示例
下面通过countdownlatch实现:"主线程"等待"5个子线程"全部都完成"指定的工作(休眠1000ms)"之后,再继续运行。

import java.util.concurrent.countdownlatch;
import java.util.concurrent.cyclicbarrier;

public class countdownlatchtest1 {

 private static int latch_size = 5;
 private static countdownlatch donesignal;
 public static void main(string[] args) {

  try {
   donesignal = new countdownlatch(latch_size);

   // 新建5个任务
   for(int i=0; i<latch_size; i++)
    new innerthread().start();

   system.out.println("main await begin.");
   // "主线程"等待线程池中5个任务的完成
   donesignal.await();

   system.out.println("main await finished.");
  } catch (interruptedexception e) {
   e.printstacktrace();
  }
 }

 static class innerthread extends thread{
  public void run() {
   try {
    thread.sleep(1000);
    system.out.println(thread.currentthread().getname() + " sleep 1000ms.");
    // 将countdownlatch的数值减1
    donesignal.countdown();
   } catch (interruptedexception e) {
    e.printstacktrace();
   }
  }
 }
}

运行结果:

main await begin.
thread-0 sleep 1000ms.
thread-2 sleep 1000ms.
thread-1 sleep 1000ms.
thread-4 sleep 1000ms.
thread-3 sleep 1000ms.
main await finished.

结果说明:主线程通过donesignal.await()等待其它线程将donesignal递减至0。其它的5个innerthread线程,每一个都通过donesignal.countdown()将donesignal的值减1;当donesignal为0时,main被唤醒后继续执行。

ps:countdownlatch和cyclicbarrier的区别:
(1) countdownlatch的作用是允许1或n个线程等待其他线程完成执行;而cyclicbarrier则是允许n个线程相互等待。
(2) countdownlatch的计数器无法被重置;cyclicbarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。