转载

源码详解CountDownLatch

源码详解CountDownLatch

CountDownLatch,是一种常见同步器。其实现依赖于AQS(可以参考抽象队列式同步器AQS详解)

具体来说一个经典得应用案例是,主线程等待子线程执行完毕,再进行信息汇总,退出主函数。

如下代码所示。我们可以大胆猜测其初始化构造,赋值计数器值,之后,每次调用countDown函数,计数器减一,当为零时,会唤醒调用await函数阻塞得线程。下面从其源码角度进行验证。

import java.util.concurrent.CountDownLatch;
public class Main{
    //初始化构造,赋值计数器值
    public static CountDownLatch cdl = new CountDownLatch(2);
    public static void main(String args[]) throws Exception {
        Thread a = new Thread(new Runnable(){
            @Override
            public void run() {
                System.out.println("Thread a working");
                try{
                    Thread.sleep(1000);
                }catch(InterruptedException e) {
                    e.printStackTrace();
                }
                cdl.countDown();
            }
        });

        Thread b = new Thread(new Runnable(){
            @Override
            public void run() {
                System.out.println("Thread b working");
                try{
                    Thread.sleep(1000);
                }catch(Exception e) {
                    e.printStackTrace();
                }
                cdl.countDown();
            }
        });
        a.start(); b.start();
        cdl.await();
        System.out.println("Main thread completed");
    }
}

源码分析

  • CountDownLatch的构造函数

    CountDownLatch构造函数,会调用其成员变量sync(AQS类型)得构造函数,Sync的构造函数,将state置为初始值

    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
    
    Sync(int count) {
    	setState(count);
    }
    
  • countDown方法

    countDown方法,会调用sync的releaseShared方法,最终会调用其父类AQS的,releaseShared方法,释放共享变量。releaseShared会调用tryReleaseShared释放资源,并且调用doReleaseShared唤醒阻塞于AQS阻塞队列的线程。

     public void countDown() {
     	sync.releaseShared(1);
     }
    
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
    

    tryReleaseShared方法,释放资源,将state值减一

    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;w
        }
    }
    
  • await方法

    调用await方法,最终会调用sync父类AQS的acquireSharedInterruptibly方法,支持可中断的获取方法。acquireSharedInterruptibly方法,包括两个过程,即tryAcquireShared,请求资源,成功则立刻返回,否则doAcquireSharedInterruptibly,进入阻塞队列等待唤醒。

    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    

    acquireSharedInterruptibly方法,支持可中断的获取方法

    public final void acquireSharedInterruptibly(int arg)
                throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
    

    tryAcquireShared函数尝试获取资源,如果state值为0,则代表计数器清零,请求成功,返回1,否则返回-1

    protected int tryAcquireShared(int acquires) {
                return (getState() == 0) ? 1 : -1;
    }
    

    获取失败,则doAcquireSharedInterruptibly,则进入阻塞队列,等待唤醒

    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);
        }
    }
    

总结

至此,已完成原理的验证,我们再次总结一下CountDownLatch的使用过程

CountDownLatch同步器的使用过程总结如下

  • 初始化时,指定计数器的值。CountDownLatch(int count)
  • 计数器值不为零时,调用await方法的线程,将会进入AQS阻塞队列,进行阻塞等待。
  • 每当有线程调用countDown方法时,会将计数器减一,同时尝试唤醒调用await方法的线程。
  • 一旦计数器的值减为0,则调用await方法的线程,将被唤醒,从阻塞点,继续执行。

·

正文到此结束
本文目录