CountDownLatch源码解析

    在看这篇博客之前,建议大家将AQS源码理清楚再来看。当然,我之前的博客中有写AQS。若你将AQS没搞懂,这里看起来就不容易理解,不然你看CountDownLatch的源码将很简单

    CountDownLatch是通过一个计数器来实现的,当我们在new 一个CountDownLatch对象的时候需要带入该计数器值,该值就表示了线程的数量。每当一个线程完成自己的任务后,计数器的值就会减1。当计数器的值变为0时,就表示所有的线程均已经完成了任务,然后就可以恢复等待的线程继续执行了。

一、成员变量

private final Sync sync;

    在CountDownLatch源码中,只有一个成员变量Sync的对象,Sync是CountDownLatch的一个内部类,这个类将是CountDownLatch功能实现的核心。

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,并重写了其中的两个方法tryAcquireShared和tryReleaseShared方法。就是获取资源和释放资源的方法。

    1、构造方法    

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

       这个构造方法就是你在构造CountDownLatch时传入的参数,也就是计数器的值。

    2、操作方法

        tryAcquireShared方法

        这个很简单,判断计数器的值是否等于0,等于0返回1,否则返回-1。

        tryReleaseShared

        方法步骤如下

            1、获取计数器的值

            2、如果计数器值为0,返回false

            3、如果计数器值不为0,则使用CAS操作将计数器值减1,如果CAS操作成功,返回当前计数器值是否等于0。

二、操作方法

    1、await方法

public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
    throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    //调用Sync类中重写的方法
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

    await方法调用了AQS的acquireSharedInterruptly(1)方法,可以看到这个方法会执行我们重写的tryAcquireShared方法

    我们重写的方法就是计数器值为0返回true(说明所有线程都已完成任务),否则返回false(说明还有线程没有完成任务)

    2、带计时器的await方法

public boolean await(long timeout, TimeUnit unit)
    throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

    这个方法和await方法一样,唯一的区别就是这个方法有时间限制,当线程没有在指定的时间内完成任务,await方法失效,所有线程都会通过await方法去执行未完的代码。

    3、countDown方法

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

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}   

 countDown方法调用了AQS的releaseShared方法。这个方法会调用我们重写的tryReleaseShared方法。

    重写的tryRelease方法以死循环的方式来确认计数器的值,先将计数器减一。当计数器为0时,返回true,不然返回false。

三、CountDownLatch的应用

public static void main(String[] args) throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(2);
    Thread t1 = new Thread(){
        @Override
        public void run() {
            System.out.println("线程A解析完成");
            latch.countDown();
        }
    };
    Thread t2 = new Thread(){
        @Override
        public void run() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程B解析完成");
            latch.countDown();
        }
    };
    t1.start();
    t2.start();
    latch.await();
    System.out.println("所有线程都已经解析完成");
}

    上述代码有两个线程去解析任务,只有当两个线程都完成解析,主线程才能去解析。

    我们创建了计数器值为2的CountDownLatch。在主线程解析的前面调用await方法,在每个线程完成解析后调用countDown方法,这样就可以达到我们所期望的要求。

猜你喜欢

转载自blog.csdn.net/yanghan1222/article/details/80280797