在过去我们实现多线程同步的代码中, 往往使用 join(),wait(),notiyAll() 等线程间通信的方式, 随着 JUC 包的不断的完善, java 为我们提供了丰富同步工具类, 官方也鼓励我们使用工具类来实现多线程的同步, 今天我们就对其中 CountDownLatch 类的使用与底层实现进行分析与总结.
一, CountDownLatch 使用
CountDownLatch 其实可以看做一个计数器, 统计多个线程执行完成的情况, 适用于控制一个或多个线程等待, 直到所有线程都执行完毕的场景, 类似与 Thread.join() 的作用. 下面我们通过一个简单的例子看下 CountDownLatch 的使用.
- public static void main(String[] args) {
- final CountDownLatch countDownLatch = new CountDownLatch(5);
- // 启动计数线程
- for (int i = 0; i < 5; i++) {
- new CountDownLatchThread(i, countDownLatch).start();
- }
- // 启动等待线程
- for (int i = 0; i < 5; i++) {
- new Thread() {
- public void run() {
- try {
- countDownLatch.await();
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- System.out.println("计数完毕了," + Thread.currentThread().getName() + "等待线程执行");
- }
- }.start();
- }
- }
计数线程代码:
- public class CountDownLatchThread extends Thread {
- private CountDownLatch countDownLatch;
- private int name;
- private int count;
- public CountDownLatchThread(int name, CountDownLatch countDownLatch) {
- this.name = name;
- this.countDownLatch = countDownLatch;
- this.count = 0;
- }
- public void run() {
- try {
- for (int i = 0; i < 10; i++) {
- Thread.sleep(100);
- count++;
- }
- System.out.println(name + "号线程 --" + Thread.currentThread().getName() + "-- 计数完成了");
- countDownLatch.countDown();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
输出结果:
1 号线程 --Thread-1-- 计数完成了
0 号线程 --Thread-0-- 计数完成了
4 号线程 --Thread-4-- 计数完成了
2 号线程 --Thread-2-- 计数完成了
3 号线程 --Thread-3-- 计数完成了
计数完毕了, Thread-5 等待线程执行
计数完毕了, Thread-6 等待线程执行
计数完毕了, Thread-7 等待线程执行
计数完毕了, Thread-8 等待线程执行
计数完毕了, Thread-9 等待线程执行
通过上面的例子可以看到, 利用 CountDownLatch 的 countDown 方法与 await() 方法, 我们可以同步计数线程与等待线程, 使等待线程在所有计数线程完成后再开始运行.
二, CountDownLatch 源码分析
接下来我们对 countDownLatch 内部源码进行一下分析.
1,CountDownLatch 的构造.
首先看下 CountDownLatch 的构造函数
- public CountDownLatch(int count) {
- if (count < 0) throw new IllegalArgumentException("count < 0");
- this.sync = new Sync(count);
- }
CountDownLatch 的构造函数会接收一个 count 值做为计数器, 也就是如果你需要等待 N 个线程执行结束, 那这里就传入 N. 同时 CountDownLatch 会实例化一个 Sync 对象, 这个 Sync 其实是 CountDownLatch 内部定义的一个继承自 AbstractQueuedSynchronizer 的实现类, 所以 CountDownLatch 提供的同步和其他功能都是围绕 Sync 这个子类实现的, 也就是基于 AbstractQueuedSynchronizer 类来实现的.
我们来看下 Sync 这个类的定义
- private static final class Sync extends AbstractQueuedSynchronizer {
- private static final long serialVersionUID = 4982264981922014374L;
- /**
- 1, 设置 AbstractQueuedSynchronizer 中同步状态的值 state, 也就是计数器的值.
- 2, 这个值 volatile 变量, 必须保证线程间的可见性;
- **/
- Sync(int count) {
- setState(count);
- }
- int getCount() {
- return getState();
- }
- // 获取同步状态的值
- protected int tryAcquireShared(int acquires) {
- return (getState() == 0) ? 1 : -1;
- }
- protected boolean tryReleaseShared(int releases) {
- // 通过 CAS 操作改变同步状态值, 保证同步状态的值 state 的线程安全.
- for (;;) {
- int c = getState();
- if (c == 0)
- return false;
- int nextc = c-1;
- if (compareAndSetState(c, nextc))
- return nextc == 0;
- }
- }
- }
2,countDown 方法
首先我们看下 countDown 方法的源码
- public void countDown() {
- // 改变同步状态值, 线程执行完成时计数器减一
- sync.releaseShared(1);
- }
AbstractQueuedSynchronizer 类中 releaseShared() 方法的源码
- public final boolean releaseShared(int arg) {
- // CountDownLatch 定义的子类 Sync 实现, 通过 CAS 操作改变 State 的值
- if (tryReleaseShared(arg)) {
- //State 以递减为 0, 代表着所有执行线程执行完毕, 共享模式下释放锁, 那么等待线程就能够拿到锁往下执行.
- doReleaseShared();
- return true;
- }
- return false;
- }
当调用 CountDownLatch 的 countDown 方法时, 就会执行计数器进行减一操作, 直到所有线程全部执行完毕, 计算器为 0 时唤醒等待线程.
AbstractQueuedSynchronizer 中 doReleaseShared 方法是执行共享模式下释放锁的操作, 从而让等待线程获取锁, 继续向下执行.
3,await 方法
- public void await() throws InterruptedException {
- sync.acquireSharedInterruptibly(1);
- }
AbstractQueuedSynchronizer 类中 acquireSharedInterruptibly() 方法的源码
- public final void acquireSharedInterruptibly(int arg)
- throws InterruptedException {
- if (Thread.interrupted())
- throw new InterruptedException();
- // 获取同步状态值
- if (tryAcquireShared(arg) < 0)
- // 同步状态值即计数器的值不为 0, 等待线程共享模式下尝试获取锁, 获取不到锁的话进入阻塞
- doAcquireSharedInterruptibly(arg);
- }
await 方法的实现也很明确, 首页获取同步状态也就是计数器的值, 如果为 0 即所有线程执行完毕返回 1, 否则返回 - 1 的话, 等待线程在共享模式下尝试获取锁, 获取不到锁的话进入阻塞;
AbstractQueuedSynchronizer 中 doAcquireSharedInterruptibly 方法是执行共享模式下获取锁的操作;
三, 总结
通过上面分析可以看到 CountDownLatch 是基于 AbstractQueuedSynchronizer 类实现的, 一个非常实用的多线程控制工具类, 它类似与一个计数器用来控制指定的线程等待, 直到计数器归零. 以上我们对 CountDownLatch 类的使用与核心方法的源码进入了一定的分析, 其中如有不足与不正确的地方还望指出与海涵.
来源: https://www.cnblogs.com/dafanjoy/p/10423219.html