一、CountDownLatch
CountDownLatch适用于需要主线程开启多个线程去并行执行,并且主线程需要等待所有子线程执行完毕后再进行汇总的场景。
使用CountDownLatch比较简单方便,可以先看下下面的实例代码:
package com.cf.test.lock;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch count = new CountDownLatch(2);
new Thread(() -> {
System.out.println("线程1启动了");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1结束");
count.countDown();
},"t1").start();
new Thread(() -> {
System.out.println("线程2启动了");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程2结束了");
count.countDown();
},"t2").start();
System.out.println("等待线程1.2结束");
count.await();
System.out.println("线程1,2结束了");
}
}
结果:
二、源码解析
2.1 继承关系
比较孤单
有一个Sync内部类,继承AQS
2.2 构造方法
只有一个带参构造
public CountDownLatch(int count) {
//小于0抛异常
if (count < 0) throw new IllegalArgumentException("count < 0");
//创建一个Sync对象并传入count参数
this.sync = new Sync(count);
}
再看一下内部类Sync
Sync(int count) {
//设置同步状态为count
setState(count);
}
可以看出Sync类将穿入的count设为了同步状态值,此时调用await()方法就会阻塞线程,子线程调用countDown()方法让state减1,所有子线程调用完之后,state=0,这是await方法就会返回。
2.3 常用方法
2.3.1 await()方法
public void await() throws InterruptedException {
//调用了AQS的内部方法
sync.acquireSharedInterruptibly(1);
}
await()方法调用了AQS中的acquireSharedInterruptibly方法
//获取共享资源时可被中断
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//如果线程中断则抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//tryAcquireShared是子类实现的,后面看
//这里是如果state不为0,进入if语句
if (tryAcquireShared(arg) < 0)
//进入AQS的等待队列
doAcquireSharedInterruptibly(arg);
}
Sync中的tryAcquireShared方法
//只是判断state是否为0
protected int tryAcquireShared(int acquires) {
//state是否等于0,为0返回1,不为0返回-1
return (getState() == 0) ? 1 : -1;
}
2.3.2 countDown()方法
public void countDown() {
sync.releaseShared(1);
}
countDown调用了AQS的releaseShared方法,而tryReleaseShared也是由子类实现的,在这里由Sync实现
public final boolean releaseShared(int arg) {
//是否state已经到0
if (tryReleaseShared(arg)) {
//释放锁,激活阻塞线程
doReleaseShared();
return true;
}
return false;
}
子类实现的tryReleaseShared方法
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
//如果c == 0,不能再减了
if (c == 0)
return false;
//原子性设置c - 1
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
三、总结
CountDownLatch的使用非常灵活和方便。底层是AQS,使用AQS的state来存放计数器。调用countDown()方法来原子性递减state的值,await()后被放入AQS的阻塞队列等待state为0后再激活返回。
在给个吃鸡:
package com.cf.test.lock;
import java.util.concurrent.CountDownLatch;
public class Player extends Thread {
private static int id = 1;
private CountDownLatch countDownLatch;
public Player(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
System.out.println("玩家" + (id++) + "加入战斗");
countDownLatch.countDown();
}
public static void main(String[] args) throws InterruptedException {
int size = 4;
CountDownLatch count = new CountDownLatch(size);
for(int i = 0 ; i < size; i++){
new Player(count).start();
}
count.await();
System.out.println("训练即将开始");
System.out.println("大吉大利,今晚吃鸡");
}
}
结果: