Chapter 8 浅析CountDownLatch类
来源:互联网 发布:g76内螺纹编程实例 编辑:程序博客网 时间:2024/06/01 10:56
1 概述
CountDownLatch类是Java的j.u.c包中的一个线程同步的辅助类,顾名思义,它可以看成一个倒计时计数器。其主要功能是让某个(或某些)线程等待另外某个(或某些)线程执行结束才接着执行。通过构造函数传入一个指定数值给CountDownLatch对象,通过调用其await方法可以使线程会陷入等待之中,直到这个CountDownLatch对象的countDown方法被调用的次数到达指定的数值为止。需要注意的是,这个CountDownLatch对象的await和countDown方法都可以在一个或多个线程中被调用。
2 典型应用
CountDownLatch类的应用场景通常是某个事件需要等待另外一些事件完成才能接着往下进行。一个典型的应用场景是运动会中的赛跑比赛,起初,各个赛道上的运动员等就位后等待裁判员的发令枪,当发令枪响起后运动员开始起跑,当所有的运动员都达到比赛的终点时,比赛结束。这里,可以抽象出两个CountDownLatch对象,第一个是一个计数为1的CountDownLatch对象,对应于裁判员的发令枪;而第二个是一个计数为运动员数目的CountDownLatch对象,对应于冲刺的终点。描述该场景的代码如下:
import java.util.Random;import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * Created by fubinhe on 16/10/27. */public class RaceDemo { private static final int RUNNER_NUM = 5; public static void main(String[] args) throws Exception { CountDownLatch cdlBegin = new CountDownLatch(1); CountDownLatch cdlArrive = new CountDownLatch(RUNNER_NUM); ExecutorService exec = Executors.newFixedThreadPool(RUNNER_NUM); for (int i = 1; i <= RUNNER_NUM; ++i) { exec.execute(new Runner(i, cdlBegin, cdlArrive)); } Thread.sleep(2000); cdlBegin.countDown(); System.out.println("race begins!"); cdlArrive.await(); System.out.println("race ends!"); }}class Runner implements Runnable { private int id; private CountDownLatch cdlBegin; private CountDownLatch cdlArrive; public Runner(int id, CountDownLatch cdlBegin, CountDownLatch cdlArrive) { this.id = id; this.cdlBegin = cdlBegin; this.cdlArrive = cdlArrive; } @Override public void run() { try { System.out.println("runner " + id + " is ready!"); cdlBegin.await(); Thread.sleep(new Random().nextInt(2000)); System.out.println("runner " + id + " has arrived!"); cdlArrive.countDown(); } catch (Exception e) { e.printStackTrace(); } }}
上述代码的运行结果如下,可以看到当5个运动员都准备好时(即5个线程都开启了),他们需要等待发令枪(即cdlBegin),而当主线程执行cdlBegin.countDown()后运动员开始起跑。然后主线程需要等待所有的运动员抵达终点才能宣布比赛结束,而所有的运动员抵达终点的标志是他们都执行了cdlArrive.countDown()。
3 源码浅析
3.1 类签名
public class CountDownLatch {}
可以看出CountDownLatch并没有继承任何类(除了Object)和实现任何接口
3.2 内部类
CountDownLatch的主要功能是在其内部维护了一个Sync的内部类对象:private final Sync sync; 如果看过ReentrantLock,可以发现实现方式很类似。
内部类Sync的源代码如下:
/** * 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; } }}
可以看出,Sync类继承自AQS(AbstractQueuedSynchronizer),而CountDownLatch对同步机制的支持主要来自Sync和AQS框架定义的函数。
3.3 主要函数
3.3.1 构造函数
CountDownLatch类的构造函数源代码如下:
/** * Constructs a {@code CountDownLatch} initialized with the given count. * * @param count the number of times {@link #countDown} must be invoked * before threads can pass through {@link #await} * @throws IllegalArgumentException if {@code count} is negative */public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count);}
可以看出,构造函数通过传一个数值初始化Sync内部类对象,如果继续查看Sync的构造函数的源代码(参考上述Sync的源代码)可以发现其构造函数的主要功能是初始化状态数,而这个状态数是定义在AQS中的。
3.3.2 await函数
await函数将会使调用该函数的线程在锁存器倒计数至零之前一直等待,除非该线程被中断,该函数的源码如下:
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1);}由源码可知,对CountDownLatch对象的await的调用会转发为对Sync的acquireSharedInterruptibly方法的调用,而该方法是从AQS中继承而来的,其源码如下:
public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg);}可以看出,该方法又调用了CountDownLatch的内部类Sync的tryAcquireShared方法(AQS中的该方法被Sync覆盖)和AQS的doAcquireSharedInterruptibly方法。tryAcquireShared函数的源码如下:
protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1;}该方法的主要功能只是简单的判断AQS的状态数(即state)是否为0,为0则返回1,不为0则返回-1。doAcquireSharedInterruptibly函数的源码如下:
/** * Acquires in shared interruptible mode. * @param arg the acquire argument */private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); }}
3.3.3 countDown函数
countDown函数将递减计数,如果计数到达零,则释放所有等待的线程,其源代码如下:
public void countDown() { sync.releaseShared(1);}其实质是调用AQS的releaseShared方法(Sync内部类没有覆盖),该方法的源代码如下:
/** * Releases in shared mode. Implemented by unblocking one or more * threads if {@link #tryReleaseShared} returns true. * * @param arg the release argument. This value is conveyed to * {@link #tryReleaseShared} but is otherwise uninterpreted * and can represent anything you like. * @return the value returned from {@link #tryReleaseShared} */public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false;}可以看出,该方法会以共享模式释放对象,并且在函数中会调用到Sync的tryReleaseShared函数(覆盖AQS的),并且根据返回结果可能会调用AQS的doReleaseShared函数,其中,tryReleaseShared源码如下:
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; }}而doReleaseShared函数的源代码如下:
/** * Release action for shared mode -- signal successor and ensure * propagation. (Note: For exclusive mode, release just amounts * to calling unparkSuccessor of head if it needs signal.) */private void doReleaseShared() { /* * Ensure that a release propagates, even if there are other * in-progress acquires/releases. This proceeds in the usual * way of trying to unparkSuccessor of head if it needs * signal. But if it does not, status is set to PROPAGATE to * ensure that upon release, propagation continues. * Additionally, we must loop in case a new node is added * while we are doing this. Also, unlike other uses of * unparkSuccessor, we need to know if CAS to reset status * fails, if so rechecking. */ for (;;) { Node h = head; if (h != null && h != tail) { int ws = h.waitStatus; if (ws == Node.SIGNAL) { if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue; // loop to recheck cases unparkSuccessor(h); } else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) continue; // loop on failed CAS } if (h == head) // loop if head changed break; }}
4 总结
通过上述的分析,可以看到CountDownLatch类的底层实现依然是AQS,对其线程所封装的特点是采用共享模式。其实现原理和ReentrantLock相似,也是通过一个继承AQS的Sync内部类实现的,而ReentrantLock里实现了共享模式和独占模式两种,默认情况下调用的是独占模式。
- Chapter 8 浅析CountDownLatch类
- CountDownLatch浅析
- Chapter 9 浅析CyclicBarrier类
- Chapter 10 浅析Semaphore类
- Chapter 11 浅析Exchanger类
- CountDownLatch使用、源码浅析
- 浅析Java中CountDownLatch用法
- 浅析Java中CountDownLatch用法
- 浅析Java中CountDownLatch用法
- 浅析Java中CountDownLatch用法
- 浅析Java中CountDownLatch用法
- 浅析Java中CountDownLatch用法
- 浅析Java中CountDownLatch用法
- 浅析Java中CountDownLatch用法
- 浅析Java中CountDownLatch用法
- 浅析Java中CountDownLatch用法
- 浅析Java中CountDownLatch用法
- 浅析Java中CountDownLatch用法
- android虚拟按键适配
- 通过控件子类化实现Edit Control接收Enter按键
- git 自动转换行符的坑爹案例
- MaskFilter
- 初识Zigbee
- Chapter 8 浅析CountDownLatch类
- 原型模式设计克隆人
- 【数据结构】红黑树
- matlab 画图
- jsvaScript apply和call方法的使用说明
- 证明$r(A^TA) = r(A)$
- poj入门水题整理6
- 如何成为更好的C++程序员?
- 组件编写3-----对象生成组件