本文转载自微信公众号《运维开发故事》,作者老郑。转载本文请联系运维开发故事公众号。功能介绍Latch是一个同步工具类,可以延迟一个线程的进度,直到它到达终止状态[CPJ3.4.2]。锁就像一扇门:在锁到达结束状态之前,门是关闭的,没有线程可以通过它。当达到结束状态时,门将打开并允许所有线程通过。当锁到达结束状态时,它不会改变状态,因此门将永远保持打开状态。闩锁可用于确保某些活动在其他活动完成之前不会继续进行,例如:确保计算在其所需的所有资源都已初始化之前不会继续进行。一个二进制锁(包括两种状态)可以用来表示“资源R已经初始化”,所有需要R的操作都必须先等待这个锁。确保服务在其依赖的所有其他服务启动后启动。每个服务都有一个关联的二进制锁。当服务S启动时,它会先等待S所依赖的其他服务的锁,待所有依赖的服务启动后释放锁S,让其他依赖S的服务继续执行。等到操作中的所有参与者(例如,多人游戏中的所有玩家)都准备就绪,然后再继续。在这种情况下,当所有玩家都准备好时,锁定将达到结束状态。CountDownLatch.jpgCountDownLatch是一种灵活的锁定实现,可用于上述情况。它可以使一个或多个线程等待一组事件的发生。锁存状态包括一个计数器,它被初始化为一个正数,指示要等待的事件数。countDown方法递减计数器,表示事件已经发生,await方法等待计数器归零,表示所有需要等待的事件都已经发生。如果计数器非零,则await阻塞直到计数器达到零,或者等待线程被中断,或者等待超时。用例TestHarness中给出了锁存器的两种常见用法。TestHarness创建一定数量的线程并使用它们并发执行指定的任务。它使用两个闩锁,分别代表“起始门”和“结束门”。起始门计数器的初始值为1,结束门计数器的初始值为工作线程数。每个工作线程做的第一件事是在启动门上等待,确保所有线程在开始执行之前就绪。每个线程做的最后一件事是将调用结束门的countDown方法减1,这样主线程就可以高效地等待,直到所有工作线程都执行完毕,这样就可以统计消耗的时间了。publicclassTestHarness{publiclongtimeTasks(intnThreads,finalRunnabletask)throwsInterruptedException{finalCountDownLatchstartGate=newCountDownLatch(1);finalCountDownLatchendGate=newCountDownLatch(nThreads);for(inti=0;iSystem.out.println(num.incrementAndGet()));System.out.println("costtime:"+time+"ms");}}//输出结果为11098756432costtime:2960900ms为什么要在TestHarness而不是线程创建后立即启动?或许,我们想测试n个线程并发执行某个任务所花费的时间。如果在创建线程后立即启动线程,则先启动的线程会“导致”后启动的线程,活动线程的数量会随着时间的推移而增减,竞争程度不断变化。开始门会让主线程实时释放所有工作线程,而结束门会让主线程等待最后一个线程执行完,而不是依次等待每个线程执行完。使用总结CountDownLatch是一次性的。计算器的值只能在构造函数中初始化一次,没有再次设置值的机制。当CountDownLatch用完后,就不能再使用了。源码分析代码分析CountDownLatch 仍然是底层由AbstractQueuedSynchronizer实现的。CountDownLatchstartGate=**new**CountDownLatch(1);我们先看看它的构造方法,创建一个sync对象。publicCountDownLatch(intcount){if(count<0)thrownewIllegalArgumentException("count<0");this.sync=newSync(count);}Sync是AbstractQueuedSynchronizer的一个实现,我们可以猜测它是根据字面意思。privatestaticfinalclassSyncextendsAbstractQueuedSynchronizer{privatestaticfinallongserialVersionUID=4982264981922014374L;//构造方法Sync(intcount){setState(count);}//获取资源数=0)?1:-1;}//释放锁protectedbooleantryReleaseShared(intreleases){//Decrementtoforsignalwhen(;;){intc=getState();if(c==0)returnfalse;intnextc=c-1;//CASunlockif(compareAndSetState(c,nextc))returnextc==0;}}}如果await方法中有计算值,则当前线程进入AQS队列生成Node节点,线程会进入阻塞状态。publicvoidawait()throwsInterruptedException{sync.acquireSharedInterruptibly(1);}其实主要是获取共享锁。publicfinalvoidacquireSharedInterruptibly(intarg)throwsInterruptedException{if(Thread.interrupted())thrownewInterruptedException();if(tryAcquireShared(arg)<0)doAcquireSharedInterruptibly(arg);}CountDownLatch.Sync实现tryAcquireShared方法,如果getState()==0返回1,否则返回-1。也就是说,await方法会在创建CountDownLatch实例后继续调用doAcquireSharedInterruptibly(arg);1;}//尝试获取锁,或者入队if(p==head){intr=tryAcquireShared(arg);if(r>=0){setHeadAndPropagate(node,r);p.next=null;//helpGCfailed=false;return;}}if(shouldParkAfterFailedAcquire(p,node)&&parkAndCheckInterrupt())thrownewInterruptedException();}}finally{if(failed)cancelAcquire(node);}}如果countDown方法中有等待线程,就会被唤醒。或者减少CountDownLatch资源的数量。publicvoidcountDown(){sync.releaseShared(1);}通过releaseShared解锁共享锁。publicfinalbooleanreleaseShared(intarg){if(tryReleaseShared(arg)){doReleaseShared();returntrue;}returnfalse;}最终会调用doReleaseShared唤醒AQS中的头节点。privatevoiddoReleaseShared(){/**Ensurethatareleasepropagates,evenifthereareother*in-progressacquires/releases.Thisproceedsintheusual*wayoftryingtounparkSuccessorofheadifitneeds*signal.Butifitdoesnot,statusissettoPROPAGATEto*ensurethatuponrelease,propagationcontinues.*Additionally,wemustloopincaseanewnodeisadded*whilewearedoingthis.Also,unlikeotherusesof*unparkSuccessor,weneedtoknowifCAStoresetstatus*fails,ifso??rechecking.*/for(;;){Nodeh=head;if(h!=null&&h!=tail){intws=h.waitStatus;if(ws==Node.SIGNAL){if(!compareAndSetWaitStatus(h,Node.SIGNAL,0))continue;//looptorecheckcasesunparkSuccessor(h);}elseif(ws==0&&!compareAndSetWaitStatus(h,0,Node.PROPAGATE))continue;//looponfailedCAS}if(h==head)//loopifheadchangedbreak;}}详细流程如下图:源码流程图CountDownLatch闭锁源码分析.png参考资料《Java 并发编程实战》https://www.cnblogs.com/Lee_xy_z/p/10470181.html