转载

CountDownLatch的实现分析

CountdownLatch 是 JDK 并发包中提供的并发工具类,其允许一个或多个线程等待其他线程完成操作。常用作将一个任务拆分成多个子任务同时执行,只有子任务都执行完毕主线程才往下执行。

使用示例

public class App implements Runnable
{
    private CountDownLatch countDownLatch;

    public App (CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    public static void main( String[] args )
    {
    	// 指定同时运行3个子任务
        int count = 3;

        CountDownLatch countDownLatch = new CountDownLatch(count);

        for (int i = 0; i < count; i++) {
            new Thread(new App(countDownLatch), "Thread-" + i).start();
        }

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
        }

        System.out.println("count down over !");
    }

    @Override
    public void run() {
        try {
            TimeUnit.SECONDS.sleep(2);

            System.out.println(Thread.currentThread().getName() + " - 执行完毕." );

        } catch (InterruptedException e) {
        } finally {
            countDownLatch.countDown();
        }
    }
}

复制代码

运行结果如下:

Thread-2 - 执行完毕.
Thread-0 - 执行完毕.
Thread-1 - 执行完毕.
count down over !
复制代码

从结果中可以看出 main 主线程会在 3 个子线程处理完毕之后才继续执行。

构造

public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}
复制代码

CountDownLatch 与其他同步组件一样,内部类 Sync 继承了 AQS,构造的时候会指定子任务个数 count , 也即是同步状态初始值。

await()

public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
复制代码

接下来看下 sync 获取共享同步状态的实现

protected int tryAcquireShared(int acquires) {
	// state == 0 的时候返回 1,反之返回 -1
	// state != 0 说明还有子任务未处理完
    return (getState() == 0) ? 1 : -1;
}
复制代码

从实现可以看出 await() 方法执行时,当子任务未处理完毕时(state != 0),调用线程会被添加到同步队列而阻塞等待。

countDown()

public void countDown() {
    sync.releaseShared(1);
}
复制代码

接下来看下 sync 的共享同步状态值释放

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;
        // 更新 state 值
        if (compareAndSetState(c, nextc))
        	// 子任务均处理完毕后,返回 true; 也就是真正的释放
        	// 将唤醒阻塞在同步队列的线程
            return nextc == 0;
    }
}
复制代码

从实现可以看出,每次子任务在调用 countDown 时,会将同步状态值减一,当所有子任务均完成时 (state = 0) 此时会唤醒阻塞在同步队列的节点。

小结

子任务在进行 countDown 操作时,最好是在 finally 块处理; 避免出现子任务处理异常,导致主线程一直阻塞的问题。

原文  https://juejin.im/post/5c15bd566fb9a049f74618b3
正文到此结束
Loading...