CountDownLatch源码分析

概述

CountDownLatch可以翻译为倒计数器,让主调用线程等待其他一些线程工作完成后,再继续运行。(相当于调用所有子线程join方法的效果)
主要有两种使用场景:
第一种设置两个信号,一个是启动信号,当控制线程发出信号以后,所有线程才开始工作,一个是全部完成的信号,当所有工作线程完成后,控制线程才继续工作。
另一个典型用法是将问题分为N个部分,每一部分用子线程跑,然后在锁存器上递减计数,当所有子线程都完成后,调用线程将能够继续运行。

例子

public static void main(String[] args) throws InterruptedException {
    final int N = 3;
    CountDownLatch startSignal = new CountDownLatch(1);
    CountDownLatch doneSignal = new CountDownLatch(N);

    for (int i = 0; i < N; ++i)
        new Thread(new Worker(startSignal, doneSignal)).start();
    doSomethingElse();            
    startSignal.countDown();      
    doSomethingElse();
    doneSignal.await();
    System.out.println(currentThread().getName() + ": end");
}

private static void doSomethingElse() {
    System.out.println(currentThread().getName() + ": do something");
}

static class Worker implements Runnable {
    private final CountDownLatch startSignal;
    private final CountDownLatch doneSignal;

    public Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
        this.startSignal = startSignal;
        this.doneSignal = doneSignal;
    }

    @Override
    public void run() {
        try {
            startSignal.await();
            doWork();
            System.out.println(currentThread().getName() + ": finish work");
            doneSignal.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    private void doWork() {
        System.out.println(currentThread().getName() + ": is working");
    }
}

运行结果:

main: do something
main: do something
Thread-1: is working
Thread-0: is working
Thread-2: is working
Thread-2: finish work
Thread-0: finish work
Thread-1: finish work
main: end

例子github地址
主线程设置了两个信号量,startSignal,doneSignal
startSignal设置的state为1,doneSignal设置的state为N需要等待的线程量
当主线程在main函数中调用startSignal的countDown方法后,所有的子线程就可以开始做自己的事了,当主线程调用doneSignal.await,开始等待,知道所有子线程完成doneSignal.countDown后,主线程继续执行。
可以想像成一个体育老师千米测试,所有学生准备就绪,等老师发出开始信号startSignal.countDown,学生们都甩开膀子跑了,老师走的千米测试的终点后,调用doneSignal.await,这时老师就要等所有学生全部跑完后,才能下课,开始自己的生活了。

CountDownLatch源码

CountDownLatch使用的是AQS的共享式占用锁,定义了一个内部类Sync继承AQS,不清楚AQS源码的需要先看下这篇文章

//只有一个入参是int的构造函数,用来初始化Sync同步器
public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}
private static final class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = 4982264981922014374L;

    Sync(int count) {
        setState(count);
    }

    int getCount() {
        return getState();
    }

    //重写了父类尝试请求加锁的方法,判断只有state为0,返回正数,即请求加锁成功
    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;
            //当state值减为0以后,共享锁释放,唤起等待节点
            if (compareAndSetState(c, nextc))
                return nextc == 0;
        }
    }
}
public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    //调用上面的尝试加锁方法
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

await方法就是判断state的值,如果state值不为0,加锁失败,进入排队等待状态。等待其他线程释放锁。

public void countDown() {
    sync.releaseShared(1);
}

countDown方法比较简单就是对state减1,当state等于0,唤起等待的线程。

你可能感兴趣的:(CountDownLatch源码分析)