应用方式
实际CountDownLatch()的作用就是协调多个线程之间等待,所有线程都结束才能继续往下走。
现实中举个例子:
三个人一起去吃饭,总有吃的快的和吃的慢的,先吃完的接着走是不是不太好看?所以需要等其他人。怎么才能做到让先吃完饭的人等还没吃完的?其实就是用的countDown()和await()方法打配合。
主要api
| 方法名 | 方法作用 | |
|---|---|---|
| CountDownLatch(int count) | 初始化,并且定义好技术器的数量 | |
| await() | 阻塞线程,一直到计数器为0为止 | |
| countDown() | 计数器减1 | |
| getCount() | 获取当前的计数 | |
| toString() | 重写过的,super.toString() + "[Count = " + sync.getCount() + "]" |
代码示例
public static void main(String[] args) throws InterruptedException {
// 定义好要被调用的总数
CountDownLatch countDownLatch = new CountDownLatch(90);
new Thread(()->{
for (int i = 0; i<30;i++){
try {
Thread.sleep(1000);
System.out.println("我是一号选手" + countDownLatch.getCount());
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
for (int i = 0; i<30;i++){
try {
Thread.sleep(1000);
System.out.println("我是老二选手" + countDownLatch.getCount());
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
for (int i = 0; i<30;i++){
try {
Thread.sleep(1000);
System.out.println("我是三号选手~" + countDownLatch.getCount());
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
System.out.println("你们三个得一起结束");
countDownLatch.await();
System.out.println("终于都结束了,over over");
}
执行结果
结果很长,就截最后那一点了
我是老二选手18
我是一号选手17
我是一号选手15
我是三号选手~15
我是老二选手15
我是一号选手12
我是三号选手~12
我是老二选手12
我是一号选手9
我是三号选手~9
我是老二选手9
我是老二选手6
我是三号选手~6
我是一号选手6
我是老二选手3
我是一号选手3
我是三号选手~3
终于都结束了,over over
Process finished with exit code 0
根预想的结果不太一样,确实一共也是90条,我以为能依次减下来,结果这跳跃式的?只能再去看源码找原因
阅读源码
CountDownLatch(int count)
可以发现他调用了一个Sync()
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
实际这是他定义的一个内部类,看来多线程的底层绕不开AQS
private static final class Sync extends AbstractQueuedSynchronizer {
private volatile int state;
Sync(int count) {
setState(count);
}
protected final void setState(int newState) {
state = newState;
}
}
发现这个计数器,是用volatile修饰的
volatile特性
- 可见性
- 有序性 所以他是没有原子性的,所以在获取的时候值未变很正常
countDown()
一样是调用了sync中的方法
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
核心是tryReleaseShared(arg),这里面是整了把乐观锁
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
compareAndSetState再往下跟代码就是native的了,其实这就是乐观锁+volatile保证数据的可靠性
结尾
原来这个计数器是乐观锁写的。。。。这次算是浅尝辄止,后面要好好的把AQS读一遍源码