基于java8分析CountDownLatch源码

ReentrantLock和ReentrantReadWriteLock都实现interface接口,内部又继承AbstractQueuedSynchronizer。而CountDownLatch是JUC中提供的一个多线程间协同的工具类,并没有实现interface接口,只在内部继承实现AQS。

CountDownLatch主要处理:主线程将某个大任务切分成数个小任务,由多个线程处理小任务,而主线程阻塞直到所有任务完成后,主线程再处理接下来的任务。即:处理一个线程与多个线程间的协同问题。

CountDownLatch内部维持的int state属性值,作为一个计数器,当计数器的值为0的时,说明所有任务都执行完毕,主任务可以继续向下执行。state可以理解为小任务的个数,甚至可以理解为线程的个数。

先来一段demo,瞅一下CountDwonLatch的用法:

public class TestCountDownLatch {
    private static final int DEFAULT = 3;
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        List<Thread> threads = new ArrayList<>();
        for (int i = 0; i < 6; i++) {
            Thread thread = new Thread(() -> {
                try {
                    TimeUnit.SECONDS.sleep(DEFAULT);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println(Thread.currentThread().getName() + "count down. ");
                    //CountDownLatch内部维持的计数器-1
                    countDownLatch.countDown();
                    System.out.println(Thread.currentThread().getName() + " " + countDownLatch.getCount());
                }
            });
            threads.add(thread);
        }
        for (Thread thread : threads) {
            thread.start();
        }
        //主线程阻塞,当内部计数器==0时,main线程将被唤醒
        System.out.println("666");
        countDownLatch.await();
        System.out.println("999");
        //state计数器不会恢复成初始参数,此时不再阻塞线程
        countDownLath.await();
        System.out.println("000);
    }
}

构造函数,count为初始化内部计数器的值:

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

CountDownLatch对外提供的使用方法:

//支持超时时间的可中断方法,阻塞主线程
public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
//子任务线程中计数器-1
public void countDown() {
    sync.releaseShared(1);
}
//获取当前计数器的值
public long getCount() {
    return sync.getCount();
}

await阻塞主线程的源码:

public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
//AQS提供的获取共享锁框架
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
        //支持线程中断时,响应异常
        if (Thread.interrupted())
            throw new InterruptedException();
        //state!=0时,为负数;则需要阻塞线程
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
}
//CountDownLatch中实现的判断条件
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}
//AQS提供的熟悉的锁框架
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;
                    }
                }
                //判断是否可以安全的阻塞,且park后检测中断状态
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            //当被中断后,需要将node节点置为null,并从双向链表中删除。如果前继是head,还需要唤醒后继节点,让其尝试获取锁
            if (failed)
                cancelAcquire(node);
        }
    }
public void countDown() {
   sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
        //根据state是否为0,来决定是否唤醒链表中的阻塞线程
        if (tryReleaseShared(arg)) {
            //
            doReleaseShared();
            return true;
        }
        return false;
    }
//state==0时,直接返回false
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;
            }
        }

private void doReleaseShared() {
          for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    //唤醒head后继节点
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

CountDownLatch基本就结束了,相对于互斥锁、读写锁,CountDownLatch就是基于AQS的一个多线程间协同工具。

CountDownLatch的特点:

1、应用于多线程协同场景中,且是单线程等待多线程任务结束

2、CountDownLatch中的state值减为0后,不会重新恢复成初始参数,因此,countDownLatch.await()被唤醒后,再次调用await()起不到阻塞线程的作用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章