写作网站哪个名声好/搜狗网站收录提交入口
CountDownLatch分析
1. 属性分析
很简单,就一个Sync属性,CountDownLatch是基于AQS来实现的。一般来说,基于AQS实现的锁或者一些别的东西,里面都有个继承与AQS的静态内部类,功能的实现都是委托给这个静态内部类来实现的。这里也是。
private final Sync sync;
2. 构造方法
count合法性检查,创建同步器(Sync)
public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count);
}
3. 主要方法分析
await
和这个类似的带等待超时的await。这里只列举一个,说明情况。
调用这个方法会使的当前线程一直等待,一直等待count变为0,或者这个线程被中断掉。如果当前的count等于0,方法会立即返回(也就是当前线程不会等待)。如果count大于0,当前线程就会等待。(其实就是被park住了),如果count变为0,或者当前线程被中断。这个方法才会返回。
public void await() throws InterruptedException {// await就是获取一个锁,注意,这里锁的模式是共享锁,在AQS里面,如果没有获取到锁,就会被park住。如果count变为0,sync.acquireSharedInterruptibly(1);
}
getCount
返回当前的count值。
public long getCount() {return sync.getCount();
}
countDown
countDown就是释放锁
public void countDown() {sync.releaseShared(1);
}
到这里,看到所有的功能是委托给Sync来实现的。下面就看看Sync具体是怎么做的
4. Sync分析
看代码,真没有多少。下面就一点点来分析是怎么做的。
也就是说,每次countDown。就是归还锁,但是Sync重写了tryReleaseShared
,理论上来说应该先获取锁,然后再释放锁,但是这里不是,先释放锁,tryReleaseShared
如果返回为true表示,释放锁成功,应该唤醒等待的节点。所以,在tryReleaseShared
里面,只有当state为0的时候才会返回true,其余的都是–。
对于await方法来说,调用的是获取锁的操作,因为在AQS里面获取锁失败会使的当前线程park住。同样的tryAcquireShared
方法返回值大于等于0 表示当前线程获取到锁,反之表示当前线程没有获取到锁,需要park。在Sync里面,只有在State变为0的时候,才会返回1。表示获取到锁。当前线程就返回。如果没有获取到锁,就阻塞。只能等待countDown了。
private static final class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 4982264981922014374L;// 在构造方法里面调用AbstractQueuedSynchronizer里面的setState方法,State表示count。Sync(int count) {setState(count);}// 直接返回Stateint getCount() {return getState();}// 如果state不是0,就返回1.表示获取到锁。protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;}protected boolean tryReleaseShared(int releases) {for (;;) {// 拿到stateint c = getState();// 如果c等于0.说明不能堵塞了。直接返回if (c == 0)return false;int nextc = c-1;// 重新设置stateif (compareAndSetState(c, nextc))return nextc == 0;}}}
这里会涉及到一个概念 AQS的独占模式和共享模式
独占模式
同一时刻,只有一个线程可以获取到锁(互斥)
共享模式
同步时刻,多个线程可以获取到锁,这个多个线程获取锁的是不一定的。(共享)
举个列子说明两个的一个小差别
@Testpublic void testCCountDownLatch() throws InterruptedException {// CountDownLatch值为2.CountDownLatch countDownLatch = new CountDownLatch(2);for (int i = 0; i < 2; i++) {new Thread(() -> {try {TimeUnit.SECONDS.sleep((int) Math.random() * 10);logMessage("start");countDownLatch.countDown();} catch (InterruptedException e) {e.printStackTrace();}}).start();}// 当state变为0之后,这四个线程会连续唤醒,如果是独占模式,就不可能发生这种情况,只能唤醒一个。for (int i = 0; i < 4; i++) {countDownLatch.await();logMessage("begin" + i);}}
public static void logMessage(Object o) {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println(simpleDateFormat.format(new Date()) + "-" + Thread.currentThread().getName() + ":" + o);}
额外补充
看上面内容的时候突然在org.apache.rocketmq.common
也有一个CountDownLatch2
。很奇妙,来看看他有什么特别的地方。
public class CountDownLatch2 {private final CountDownLatch2.Sync sync;public CountDownLatch2(int count) {if (count < 0) {throw new IllegalArgumentException("count < 0");} else {this.sync = new CountDownLatch2.Sync(count);}}public void await() throws InterruptedException {this.sync.acquireSharedInterruptibly(1);}public boolean await(long timeout, TimeUnit unit) throws InterruptedException {return this.sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));}public void countDown() {this.sync.releaseShared(1);}public long getCount() {return (long)this.sync.getCount();}public void reset() {this.sync.reset();}public String toString() {return super.toString() + "[Count = " + this.sync.getCount() + "]";}private static final class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 4982264981922014374L;private final int startCount;Sync(int count) {this.startCount = count;this.setState(count);}int getCount() {return this.getState();}protected int tryAcquireShared(int acquires) {return this.getState() == 0 ? 1 : -1;}protected boolean tryReleaseShared(int releases) {int c;int nextc;do {c = this.getState();if (c == 0) {return false;}nextc = c - 1;} while(!this.compareAndSetState(c, nextc));return nextc == 0;}protected void reset() {this.setState(this.startCount);}}
}
百分之90都是一样的,这里增加了一个reset
方法。用于将state变为一开始的值。也就是说,在countDown一段之后,可以重新将state设置为初始状态。重新设置值在CountDownLatch
里面是不支持的。这个值只有在new的时候才能设置,别的地方没有这个操作。
关于CountDownLatch的分析就分析到这里了。 如有不正确的地方,欢迎指出。谢谢。