简介

CountDownLatch是Java并发编程中的一个 同步辅助东西 允许一个或多个线程等候在其他线程中履行的一组操作完结。。 用来和谐不同线程程之间的使命同步。一般用于将一个复杂使命依照不同的履行顺序拆分成多个彼此独立的子使命履行,其中某个线程需要等候其他线程履行完结后才能履行。

源码剖析之CountDownLatch内部是实现原理

运用步骤

1 .界说两个CountDownLatch目标,别离作用于不同的主线程和子线程。

源码剖析之CountDownLatch内部是实现原理

2.创立多个子线程使命并履行。

源码剖析之CountDownLatch内部是实现原理

3.别离将两个CountDownLatch目标的实例传入子线程使命中。

源码剖析之CountDownLatch内部是实现原理

4.主线程中先后调用主线程使命履行。

源码剖析之CountDownLatch内部是实现原理

源码剖析之CountDownLatch内部是实现原理

5.根据使命履行,打印成果。

源码剖析之CountDownLatch内部是实现原理

6.履行过程详解

先开启子线程履行使命,但是在子线程Work的run办法中startSignal.await();挂起等候。主线程履行完第一个doSomethingElse()使命,然后调用startSignal.countDown(),开释同步锁,此时子线程得到了履行的机会。主线程调用doneSignal.await()办法堵塞当时线程,直到一切子线程履行完结。调用downSignal.countDown(),将同步锁计数减到0,主线程恢复履行。履行最后一个doSomethingElse()使命。


源码分析

完成原理

CountDownLatch内部界说了一个静态类Sync承继于行列同步器AbstractQueuedSynchronizer(简称AQS),AQS是用来构建同步锁的基本结构,它内部运用一个Int类型的state表示同步状况、,经过内置的FIFO行列来完结资源获取的线程的排队作业。

源码剖析之CountDownLatch内部是实现原理

规划形式

AQS的规划基于模板办法的规划形式。模板办法是一种行为规划形式,在父类中界说了一些算法结构,子类在完成时不能修正算法结构,只能重写特定的步骤。

中心办法1-countDown办法

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

作用:减少锁存器的计数,假如计数为0,则开释一切等候的线程,假如当时计数大于0,则依次递减。

CountDown内部调用sync的releaseShared办法进行锁的开释。

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

sync.releaseShared办法其实是AQS的办法,在AQS的releaseShared办法中,tryReleaseShared作为一个抽象办法有子类详细完成,然后调用 doReleaseShared()。

由于Sync承继与AQS,一切内部完成了tryReleaseShared办法。

protected boolean tryReleaseShared(int releases) {
    //经过循环遍历,开释锁资源
    for (;;) {
        int c = getState();
         if (c == 0)
            return false;
         int nextc = c-1;
        if (compareAndSetState(c, nextc))
         return nextc == 0;
            }
        }

Sync内部经过循环遍历,对锁的同步状况进行比较。

doReleaseShared()的详细完成在AQS中。

 private void doReleaseShared() {
         //循环
        for (;;) {
            Node h = head;
            if (h != null && h != tail) { //假如队链表不为null
                int ws = h.waitStatus;//获取节点的等候状况
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            //CAS操作失利,持续
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                //当时状况为0,且CAS失利,则持续
            }
            if (h == head)                   //假如只剩头节点,则跳出循环
                break;
        }
    }

总结await的履行过程如下:

源码剖析之CountDownLatch内部是实现原理

中心办法2-await办法

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

await办法调用sync的acquireSharedInterruptibly(1)。acquireSharedInterruptibly办法在AQS内部。

public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        //假如线程中止
        if (Thread.interrupted())
            //抛出反常
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)//状况为-1,
            //进入等候行列
            doAcquireSharedInterruptibly(arg);
    }

当时线程假如没有中止,则经过tryAcquireShared判断状况。

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

当时线程未获取到锁资源,进入等候行列,履行doAcquireSharedInterruptibly办法。

 private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (nanosTimeout <= 0L)
            return false;
        final long deadline = System.nanoTime() + nanosTimeout;
        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 true;
                    }
                }
                nanosTimeout = deadline - System.nanoTime();
                if (nanosTimeout <= 0L)
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);
                if (Thread.interrupted()) //获取失利,中止线程
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

在AQS中调用 setHeadAndPropagate(node, r),设置头节点状况。

 private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; 
        setHead(node);//设置头节点
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;//获取下一个节点
            if (s == null || s.isShared()) //假如后一个节点为null,或为共享状况,则开释
                doReleaseShared();
        }
    }

假如当时只有一个头节点,则履行AQS中的doReleaseShared

 private void doReleaseShared() {
         //循环
        for (;;) {
            Node h = head;
            if (h != null && h != tail) { //假如队链表不为null
                int ws = h.waitStatus;//获取节点的等候状况
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            //CAS操作失利,持续
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                //当时状况为0,且CAS失利,则持续
            }
            if (h == head)                   //假如只剩头节点,则跳出循环
                break;
        }
    }

总结await的履行过程如下:

源码剖析之CountDownLatch内部是实现原理

总结

CountDownLatch作为并发同步东西,其底层运用AQS结构完成的,一般用于对多个线程之间使命的交替履行或许使命之间详细关联性。用于控制多个线程的履行顺序。本文正在参与「金石计划」