网站建设行业资讯,免费软件追剧,_网站建设网站,商业空间设计师岗位职责一、定义 CountDownLatch的作用很简单#xff0c;就是一个或者一组线程在开始执行操作之前#xff0c;必须要等到其他线程执行完才可以。我们举一个例子来说明#xff0c;在考试的时候#xff0c;老师必须要等到所有人交了试卷才可以走。此时老师就相当于等待线程#xff…一、定义 CountDownLatch的作用很简单就是一个或者一组线程在开始执行操作之前必须要等到其他线程执行完才可以。我们举一个例子来说明在考试的时候老师必须要等到所有人交了试卷才可以走。此时老师就相当于等待线程而学生就好比是执行的线程。
注意java中还有一个同步工具类叫做CyclicBarrier他的作用和CountDownLatch类似。同样是等待其他线程都完成了才可以进行下一步操作我们再举一个例子在打王者的时候在开局前所有人都必须要加载到100%才可以进入。否则所有玩家都相互等待。
我们看一下区别
CountDownLatch: 一个线程(或者多个) 等待另外N个线程完成某个事情之后才能执行。 CyclicBarrier : N个线程相互等待任何一个线程完成之前所有的线程都必须等待。关键点其实就在于那N个线程1CountDownLatch里面N个线程就是学生学生做完了试卷就可以走了不用等待其他的学生是否完成2CyclicBarrier 里面N个线程就是所有的游戏玩家一个游戏玩家加载到100%还不可以必须要等到其他的游戏玩家都加载到100%才可以开局
现在应该理解CountDownLatch的含义了吧下面我们使用一个代码案例来解释。
二、使用 我们使用学生考试的案例来进行演示
我们定义了一个CountDownLatch并设置其值为2。有两个学生使用两个线程来表示然后依次执行。最后老师线程main线程在学生线程都执行完了才可以执行。我们来运行一边看看结果。 public void mysqlTest() {
CountDownLatch countDownLatchnew CountDownLatch(2);System.out.println(全班同学开始考试一共2个同学);new Thread(()-{System.out.println(第一个同学交卷countDownLatch-1);countDownLatch.countDown();}).start();new Thread(()-{System.out.println(第二个同学交卷countDownLatch-1);countDownLatch.countDown();}).start();try {countDownLatch.await();} catch (Exception e) {e.printStackTrace();}System.out.println(老师清点完试卷只要有一个学生未交卷只要countDownLatch不为0所有考试不得离场);}在上面我们定义了一个CountDownLatch并设置其值为2。有两个学生使用两个线程来表示然后依次执行。最后老师线程main线程在学生线程都执行完了才可以执行。 在上面我们的等待线程时老师main线程CountDownLatch从2见减到0主线程才执行。 下面我们对这个countDownLatch分析一下。为什么具有上面的特点。
三、原理 在上面我们看到CountDownLatch主要使用countDown方法进行减1操作使用await方法进行等到操作。我们进入到源码中看看。本源码基于jdk1.8。特在此说明。
1、countDown原理
/*** Decrements the count of the latch, releasing all waiting threads if* the count reaches zero.** pIf the current count is greater than zero then it is decremented.* If the new count is zero then all waiting threads are re-enabled for* thread scheduling purposes.** pIf the current count equals zero then nothing happens.*/
public void countDown() {sync.releaseShared(1);
}CountDownLatch里面保存了一个count值通过减1操作直到为0时候等待线程才可以执行。而且通过源码也可以看到这个countDown方法其实是通过sync调用releaseShared(1)来完成的。
OK。到了这一步就疑问sync是什么releaseShared方法又是如何实现的。我们不妨接着看源码在CountDownLatch的开头我们找到了答案原来这个sync在这里定义了。
public class CountDownLatch {/*** Synchronization control For CountDownLatch.* Uses AQS state to represent 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) {return (getState() 0) ? 1 : -1;}protected boolean tryReleaseShared(int releases) {// Decrement count; signal when transition to zerofor (;;) {int c getState();if (c 0)return false;int nextc c-1;if (compareAndSetState(c, nextc))return nextc 0;}}}在这里我们发现继承了AbstractQueuedSynchronizerAQS。AQS的其中一个作用就是维护线程状态和获取释放锁。在这里也就是说CountDownLatch使用AQS机制维护锁状态。而releaseShared(1)方法就是释放了一个共享锁。
现在理解了吧底层使用AQS机制调用releaseShared方法释放一个锁资源。那么等待的方法是如何实现的呢
2、await原理 public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);}public boolean await(long timeout, TimeUnit unit)throws InterruptedException {return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));}这俩方法都是让线程等待第一个没有实现限制第二个有时间限制
1await()
await()底层主要是acquireSharedInterruptibly方法实现的继续跟进去看看。 public final void acquireSharedInterruptibly(int arg)throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();if (tryAcquireShared(arg) 0)doAcquireSharedInterruptibly(arg);}这里面有两个if语句首先第一个判断是否被中断如果被中断了那就抛出中断异常。然后判断当前是否还有线程未执行如果有那就那就执行doAcquireSharedInterruptibly方法继续等待。
protected int tryAcquireShared(int arg) { throw new UnsupportedOperationException(); }
上面函数的意思 1、这是aqs里的方法 2、arg在这里调用的是1标识countDown是否减少到了0 3、如果到了0说明满足要求返回1不在等待 4、如果未达到0说明还有线程未执行必须等待 5、执行结束才可以返回-1此时小于0执行doAcquireSharedInterruptibly方法 下面我们就来看看这个doAcquireSharedInterruptibly是如何实现的。 private void doAcquireSharedInterruptibly(int arg)throws InterruptedException {final Node node addWaiter(Node.SHARED);boolean failed true;try {for (;;) {final Node p node.predecessor();if (p head) {int r tryAcquireShared(arg);if (r 0) {setHeadAndPropagate(node, r);p.next null; // help GCfailed false;return;}}if (shouldParkAfterFailedAcquire(p, node) parkAndCheckInterrupt())throw new InterruptedException();}} finally {if (failed)cancelAcquire(node);}}大致意思他会用一个一个的节点将线程串起来 等达到条件后再一个一个的唤醒。核心就是第三行的addWaiter函数。我们可以再跟进去看看吧。
private Node addWaiter(Node mode) {Node node new Node(Thread.currentThread(), mode);// Try the fast path of enq; backup to full enq on failureNode pred tail;if (pred ! null) {node.prev pred;if (compareAndSetTail(pred, node)) {pred.next node;return node;}}enq(node);return node;
}你会发现这里面也使用了CAS机制。而且就是使用链表穿起来的。
2 await(long timeout, TimeUnit unit)
这个方法的意思是等待指定的时间如果还有线程没执行完那就接着执行。就好比考完试了还有同学没交试卷此时因为到时间了。不管三七二十一也不管剩下的同学是否提交直接就走了。其底层是通过Sync的tryAcquireSharedNanos方法实现的我们接着进入到源码中看看。 public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();return tryAcquireShared(arg) 0 ||doAcquireSharedNanos(arg, nanosTimeout);}在这里皮球又一次被踢走了真正实现的其实就是doAcquireSharedNanos方法tryAcquireShared方法主要是判断是否当前满足wait的条件。我们接着看。 private boolean doAcquireSharedNanos(int arg, long nanosTimeout)throws InterruptedException {if (nanosTimeout 0L)return false;final long deadline System.nanoTime() nanosTimeout;final Node node addWaiter(Node.SHARED);boolean failed true;try {for (;;) {final Node p node.predecessor();if (p head) {int r tryAcquireShared(arg);if (r 0) {setHeadAndPropagate(node, r);p.next null; // help GCfailed false;return true;}}nanosTimeout deadline - System.nanoTime();if (nanosTimeout 0L)return false;if (shouldParkAfterFailedAcquire(p, node) nanosTimeout spinForTimeoutThreshold)LockSupport.parkNanos(this, nanosTimeout);if (Thread.interrupted())throw new InterruptedException();}} finally {if (failed)cancelAcquire(node);}}上面的代码看似长最核心的就是for循环里面的最主要的意思就是如果当前还有线程未执行而且过了超时时间那就直接执行等待线程就好了不再等了。也就是我在指定的时间内你没执行完我等着你要是超了这个时间点我就不管了。
对于CountDownLatch来说原理主要还是通过源码来认识。不过CountDownLatch看起来虽然很好用也有很多不足之处比如说CountDownLatch是一次性的 , 计数器的值只能在构造方法中初始化一次 , 之后没有任何机制再次对其设置值当CountDownLatch使用完毕后 , 它不能再次被使用。