CountDown
基本使用
CountDownLatch:计数器,用来进行线程同步协作,等待所有线程完成,让一些线程阻塞直到另一些线程完成一系列操作才被唤醒
构造器:
public CountDownLatch(int count)
:初始化唤醒需要的 down 几步
常用API:
public void await()
:让当前线程等待,必须 down 完初始化的数字才可以被唤醒,否则进入无限等待public void countDown()
:计数器进行减 1(down 1)public void await(3, TimeUnit.SECONDS)
:限时等待
应用:同步等待多个 Rest 远程调用结束
1 | // LOL 10人进入游戏倒计时 |
面试
面试官:看你简历上有写熟悉并发编程,CountDownLatch一定用过吧,跟我说说它!
我:CountDownLatch是JDK提供的一个同步工具,它可以让一个或多个线程等待,一直等到其他线程中执行完成一组操作。
面试官:CountDownLatch有哪些常用的方法?
我:有countDown
方法和await
方法,CountDownLatch在初始化时,需要指定用给定一个整数作为计数器。当调用countDown
方法时,计数器会被减1;当调用await
方法时,如果计数器大于0时,线程会被阻塞,一直到计数器被countDown
方法减到0时,线程才会继续执行。计数器是无法重置的,当计数器被减到0时,调用await
方法都会直接返回。
面试官:调用
countDown
方法时,线程也会阻塞嘛?
我:不会的,调用countDown
的线程可以继续执行,不需要等待计数器被减到0,只是调用await方法的线程需要等待。
面试官:CountDownLatch的实现原理是什么?
我:CountDownLatch有一个内部类叫做Sync,它继承了AbstractQueuedSynchronizer类,其中维护了一个整数state
,并且保证了修改state
的可见性和原子性。
创建CountDownLatch实例时,也会创建一个Sync的实例,同时把计数器的值传给Sync实例,具体是这样的:
1 | public CountDownLatch(int count) { |
在 countDown
方法中,只调用了Sync实例的releaseShared
方法,具体是这样的:
1 | public void countDown() { |
其中的releaseShared
方法,先对计数器进行减1操作,如果减1后的计数器为0,唤醒被await方法阻塞的所有线程,具体是这样的:
1 | public final boolean releaseShared(int arg) { |
其中的tryReleaseShared
方法,先获取当前计数器的值,如果计数器为0时,就直接返回;如果不为0时,使用CAS方法对计数器进行减1操作,具体是这样的:
1 | protected boolean tryReleaseShared(int releases) { |
在await
方法中,只调用了Sync实例的acquireSharedInterruptibly
方法,具体是这样的:
1 | public void await() throws InterruptedException { |
其中acquireSharedInterruptibly
方法,判断计数器是否为0,如果不为0则阻塞当前线程,具体是这样的:
1 | public final void acquireSharedInterruptibly(int arg) |
其中tryAcquireShared
方法,是AbstractQueuedSynchronizer中的一个模板方法,其具体实现在Sync类中,其主要是判断计数器是否为零,如果为零则返回1,如果不为零则返回-1,具体是这样的:
1 | protected int tryAcquireShared(int acquires) { |