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()起不到阻塞线程的作用