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

直译过来就是倒计数(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类图如下:

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。

时间: 2016-07-10

Java多线程编程之CountDownLatch同步工具使用实例

好像倒计时计数器,调用CountDownLatch对象的countDown方法就将计数器减1,当到达0时,所有等待者就开始执行. java.util.concurrent.CountDownLatch 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待.用给定的计数初始化CountDownLatch.由于调用了countDown()方法,所以在当前计数到达零之前,await方法会一直受阻塞.之后,会释放所有等待的线程,await的所有后续调用都将立即返回.这种现

Java concurrency之AtomicReference原子类_动力节点Java学院整理

AtomicReference介绍和函数列表 AtomicReference是作用是对"对象"进行原子操作. AtomicReference函数列表 // 使用 null 初始值创建新的 AtomicReference. AtomicReference() // 使用给定的初始值创建新的 AtomicReference. AtomicReference(V initialValue) // 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值. boolean compare

Java concurrency之非公平锁_动力节点Java学院整理

获取非公平锁(基于JDK1.7.0_40) 非公平锁和公平锁在获取锁的方法上,流程是一样的:它们的区别主要表现在"尝试获取锁的机制不同".简单点说,"公平锁"在每次尝试获取锁时,都是采用公平策略(根据等待队列依次排序等待):而"非公平锁"在每次尝试获取锁时,都是采用的非公平策略(无视等待队列,直接尝试获取锁,如果锁是空闲的,即可获取状态,则获取锁). 1. lock() lock()在ReentrantLock.java的NonfairSync类

Java concurrency之公平锁(二)_动力节点Java学院整理

释放公平锁(基于JDK1.7.0_40) 1. unlock() unlock()在ReentrantLock.java中实现的,源码如下: public void unlock() { sync.release(1); } 说明: unlock()是解锁函数,它是通过AQS的release()函数来实现的. 在这里,"1"的含义和"获取锁的函数acquire(1)的含义"一样,它是设置"释放锁的状态"的参数.由于"公平锁"是

java线程并发countdownlatch类使用示例

复制代码 代码如下: package com.yao; import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; /** * CountDownLatch是个计数器,它有一个初始数, * 等待这个计数器的线程必须等到计数器倒数到零时才可继续. */public class CountDownLatchTe

Java concurrency集合之CopyOnWriteArraySet_动力节点Java学院整理

CopyOnWriteArraySet介绍 它是线程安全的无序的集合,可以将它理解成线程安全的HashSet.有意思的是,CopyOnWriteArraySet和HashSet虽然都继承于共同的父类AbstractSet:但是,HashSet是通过"散列表(HashMap)"实现的,而CopyOnWriteArraySet则是通过"动态数组(CopyOnWriteArrayList)"实现的,并不是散列表. 和CopyOnWriteArrayList类似,CopyO

Java concurrency之Condition条件_动力节点Java学院整理

Condition介绍 Condition的作用是对锁进行更精确的控制.Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法.不同的是,Object中的wait(),notify(),notifyAll()方法是和"同步锁"(synchronized关键字)捆绑使用的:而Condition

Java concurrency之CountDownLatch原理和示例_动力节点Java学院整理

CountDownLatch简介 CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. CountDownLatch和CyclicBarrier的区别 (01) CountDownLatch的作用是允许1或N个线程等待其他线程完成执行:而CyclicBarrier则是允许N个线程相互等待. (02) CountDownLatch的计数器无法被重置:CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier

Java concurrency之共享锁和ReentrantReadWriteLock_动力节点Java学院整理

ReadWriteLock 和 ReentrantReadWriteLock介绍 ReadWriteLock,顾名思义,是读写锁.它维护了一对相关的锁 - - "读取锁"和"写入锁",一个用于读取操作,另一个用于写入操作. "读取锁"用于只读操作,它是"共享锁",能同时被多个线程获取. "写入锁"用于写入操作,它是"独占锁",写入锁只能被一个线程锁获取. 注意:不能同时存在读取锁和写入锁

Java concurrency之AtomicLong原子类_动力节点Java学院整理

AtomicLong介绍和函数列表 AtomicLong是作用是对长整形进行原子操作. 在32位操作系统中,64位的long 和 double 变量由于会被JVM当作两个分离的32位来进行操作,所以不具有原子性.而使用AtomicLong能让long的操作保持原子型. AtomicLong函数列表 // 构造函数 AtomicLong() // 创建值为initialValue的AtomicLong对象 AtomicLong(long initialValue) // 以原子方式设置当前值为ne

Java concurrency之AtomicLongArray原子类_动力节点Java学院整理

AtomicLongArray介绍和函数列表  AtomicLongArray函数列表 // 创建给定长度的新 AtomicLongArray. AtomicLongArray(int length) // 创建与给定数组具有相同长度的新 AtomicLongArray,并从给定数组复制其所有元素. AtomicLongArray(long[] array) // 以原子方式将给定值添加到索引 i 的元素. long addAndGet(int i, long delta) // 如果当前值 =

Java concurrency之AtomicLongFieldUpdater原子类_动力节点Java学院整理

AtomicLongFieldUpdater介绍和函数列表 AtomicLongFieldUpdater可以对指定"类的 'volatile long'类型的成员"进行原子更新.它是基于反射原理实现的. AtomicLongFieldUpdater函数列表 // 受保护的无操作构造方法,供子类使用. protected AtomicLongFieldUpdater() // 以原子方式将给定值添加到此更新器管理的给定对象的字段的当前值. long addAndGet(T obj, lo

Java concurrency之公平锁(一)_动力节点Java学院整理

基本概念 本章,我们会讲解"线程获取公平锁"的原理:在讲解之前,需要了解几个基本概念.后面的内容,都是基于这些概念的:这些概念可能比较枯燥,但从这些概念中,能窥见"java锁"的一些架构,这对我们了解锁是有帮助的. 1. AQS -- 指AbstractQueuedSynchronizer类.     AQS是java中管理"锁"的抽象类,锁的许多公共方法都是在这个类中实现.AQS是独占锁(例如,ReentrantLock)和共享锁(例如,Sem

Java JVM原理与调优_动力节点Java学院整理

JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的.Java虚拟机包括一套字节码指令集.一组寄存器.一个栈.一个垃圾回收堆和一个存储方法域. JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行.是运行Java应用最底层部分. JDK(Java Development kit) 整个Java的核心,包括了Java运行环境(Java Runtime E

Java中Object toString方法简介_动力节点Java学院整理

一.Object类介绍  Object类在Java里面是一个比较特殊的类,JAVA只支持单继承,子类只能从一个父类来继承,如果父类又是从另外一个父类继承过来,那他也只能有一个父类,父类再有父类,那也只能有一个,JAVA为了组织这个类组织得比较方便,它提供了一个最根上的类,相当于所有的类都是从这个类继承,这个类就叫Object.所以Object类是所有JAVA类的根基类,是所有JAVA类的老祖宗.所有的类,不管是谁,都是从它继承下来的. 二.toString方法介绍  一个字符串和另外一种类型连接

Java IO流体系继承结构图_动力节点Java学院整理

Java IO体系结构看似庞大复杂,其实有规律可循,要弄清楚其结构,需要明白两点: 1. 其对称性质:InputStream 与 OutputStream, Reader 与 Writer,他们分别是一套字节输入-输出,字符输入-输出体系 2. 原始处理器(适配器)与链接流处理器(装饰器) 其结构图如下: Reader-Writer体系 1. 基类 InputStream与OutputStream是所有字节型输入输出流的基抽象类,同时也是适配器(原始流处理器)需要适配的对象,也是装饰器(链接流处