countDownLatch是一个计数器,线程完成一个记录一个,计数器递减,只能只用一次。
查看CountDownLatch的构造函数
// 传入一个大于0的资源信息
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
内部持有一个静态内部类,重写同步资源获取和释放逻辑
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) {
// 如果当前的资源数为0表示获取同步资源成功
// 对于tryAcquireShared方法来说,返回的int类型只要>=0就表示获取
// 锁资源成功
return (getState() == 0) ? 1 : -1;
}
// 共享锁式释放锁
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
// 如果没有资源了,返回释放锁失败
if (c == 0)
return false;
// 剩余的资源个数
int nextc = c-1;
// cas设置剩余的资源
if (compareAndSetState(c, nextc))
// 如果剩余数为0个,表示资源释放成功
return nextc == 0;
}
}
}
看下countDown方法
public void countDown() {
// 共享式释放1个资源
sync.releaseShared(1);
}
看下getCount方法
public long getCount() {
// 返回当前还剩余的资源数
return sync.getCount();
}
看下await方法
public void await() throws InterruptedException {
// 这里调用AQS的模板方法,看下这个方法
sync.acquireSharedInterruptibly(1);
}
// 该方法响应线程中断
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 如果获取锁失败,进入doAcquireSharedInterruptibly方法
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
// 看下这个方法
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
// 当前线程加入等待队列
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
// cas自旋获取锁资源,一直在尝试
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);
}
}
总结:在调用countDown()方法的时候调用了tryReleaseShare()方法,会将共享资源减一操作,在调用await()方法的时候调用了tryAcquireShared()方法,它的获取到资源的逻辑是【return (getState() == 0) ? 1 : -1】,可以看到只有state为0的时候它才会获取锁成功,同样它也是共享式获取,说明可以多个线程等待。
使用示例
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
// 初始2个资源
CountDownLatch cdl = new CountDownLatch(2);
Thread t1 = new Thread(()->{
try {
Thread.sleep(3000);
System.out.println("t1 exe...");
} catch (InterruptedException e) {
e.printStackTrace();
}
// 资源数减1
cdl.countDown();
},"t1");
Thread t2 = new Thread(()->{
try {
Thread.sleep(5000);
System.out.println("t2 exe...");
} catch (InterruptedException e) {
e.printStackTrace();
}
// 资源数减1
cdl.countDown();
},"t2");
t1.start();
t2.start();
cdl.await();
System.out.println("main exe...");
}
}
打印结果
t1 exe...
t2 exe...
main exe...
可以看到t1和t2执行结束后main线程才执行