建设个人信息网站,网站建设的工作描述,手机做简单的网站,网站打开速度进行检测1 缘起
上篇文章讲到了CountDownLatch#xff1a;https://blog.csdn.net/Xin_101/article/details/129116170 作为同系的佼佼者#xff0c;不得不提CyclicBarrier#xff0c; 设计理念相似#xff0c;都是多线程等待#xff0c;但是#xff0c;应用的技术以及功能不同https://blog.csdn.net/Xin_101/article/details/129116170 作为同系的佼佼者不得不提CyclicBarrier 设计理念相似都是多线程等待但是应用的技术以及功能不同 下面根据源码及实例讲解 帮助读者轻松应对知识交流与考核。
2 CyclicBarrier
同步辅助工具允许一组线程相互等待对方达到共同的屏障点。 CyclicBarrier在固定大小的线程组中非常有用这些线程组可以偶尔彼此等待。 这个屏障被称为循环屏障因为释放等待线程后可以重新使用线程和屏障点。 CyclicBarrier支持Runnable可选命令这个Runnable介于到达屏障点前和突破屏障点后在每个屏障点执行一次。 Runnable屏障点操作对任何一方继续之前更新共享状态非常有用。 内存一致性影响先调用await方法的线程动作发生在其他部分屏障操作之前先于其他线程通过await方法获取响应结果。
2.1 测试样例
使用CyclicBarrier有两个关键初始化和await 其中初始化有两种方式第一使用Runnable当到达屏障时执行第二不使用Runnable await线程等待当同步状态为0时唤醒线程释放锁突破屏障继续执行后面的逻辑。 同时CyclicBarrier是可重用的先给一个测试的样例。
package com.monkey.java_study.juc;import org.apache.commons.lang3.time.StopWatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.Random;
import java.util.concurrent.CyclicBarrier;/*** 测试CyclicBarrier.** author xindaqi* since 2023-02-20 15:48*/
public class CyclicBarrierTest {private static final Logger logger LoggerFactory.getLogger(CyclicBarrierTest.class);static class ThreadRunner implements Runnable {private final CyclicBarrier cyclicBarrier;public ThreadRunner(CyclicBarrier cyclicBarrier) {this.cyclicBarrier cyclicBarrier;}Overridepublic void run() {Random random new Random();int randomBound 1000;try {// 第一个屏障StopWatch stopWatch new StopWatch();stopWatch.start();Thread.sleep(random.nextInt(randomBound));stopWatch.stop();logger.info({} 到达第一个屏障, time cost:{}, Thread.currentThread().getName(), stopWatch.formatTime());cyclicBarrier.await();logger.info({} 突破第一个屏障, time cost:{}, Thread.currentThread().getName(), stopWatch.formatTime());// 第二个屏障stopWatch.reset();stopWatch.start();Thread.sleep(random.nextInt(randomBound));stopWatch.stop();logger.info({} 到达第二个屏障, time cost:{}, Thread.currentThread().getName(), stopWatch.formatTime());cyclicBarrier.await();logger.info({} 突破第二个屏障, time cost:{}, Thread.currentThread().getName(), stopWatch.formatTime());} catch (Exception ex) {throw new RuntimeException(ex);}}}public static void main(String[] args) {StopWatch stopWatch new StopWatch();stopWatch.start();CyclicBarrier cyclicBarrier new CyclicBarrier(3, () - {logger.info(开始突破屏障.);});for (int i 0; i 3; i) {new Thread(new ThreadRunner(cyclicBarrier)).start();}}
}3 源码分析
先看下CyclicBarrier的两个属性lock和trip 源码如下图所示其中lock为ReentrantLocktrip为Condition 由Condition可知通过await可实现线程等待释放锁 CyclicBarrier通过这个技术实现线程等待线程唤醒实现屏障的功能。
3.1 初始化
先看CyclicBarrier初始化源码如下图所示 由图可知参数有两个同步状态数量和Runnable 其中同步状态数量表示等待线程的数量Runnable用于到达屏障时执行逻辑。 位置java.util.concurrent.CyclicBarrier#CyclicBarrier(int, java.lang.Runnable)
下面的初始化不指定Runnable源码如下图所示 由源码可知只指定了同步状态数量而Runnable则为null后续不会执行其他逻辑。 位置java.util.concurrent.CyclicBarrier#CyclicBarrier(int)
3.2 await
完成CyclicBarrier的初始化 接下来需要维护屏障开启线程等待 通过await方法实现源码如下图所示。 await有两种带参和不带参 不带参的源码如下图所示。 位置java.util.concurrent.CyclicBarrier#await()
带参方法源码如下图所示由图可知 参数为超时时间是线程等待的最大时间线程等待超时后抛出异常。 具体的实现还要看dowait后面接着分析。 位置java.util.concurrent.CyclicBarrier#await(long, java.util.concurrent.TimeUnit)
3.2.1 dowait
dowait源码如下图所示源码比较长分段分析 源码中标识了解析。 位置java.util.concurrent.CyclicBarrier#dowait
第一段源码如下图所示 由源码可知CyclicBarrier使用了可重入锁Reentrant 一般而言多线程使用ReentrantLock会阻塞线程进入队列排队 但是正如ReentrantLock设计对线程的使用提供了更高的灵活性 可以通过await让线程等待并释放锁通过signalAll()唤醒等待的线程 于是CyclicBarrier多线程不会进入阻塞而是线程进入等待状态然后释放锁 这样其他线程可以不用等待其他已经获得锁的线程释放锁就可以获取锁因为其他线程进入等待状态并且释放了锁。 这部分的逻辑通过配置generation作为突破标识 并且使同步状态减1同步状态为0时进入对应的逻辑后面讲。
3.2.1.1 获取锁 3.2.1.2 冲破屏障
接下来进入同步状态为0的逻辑源码如下图所示 由图可知 同步状态为0时说明所有线程均已执行并且最后一进入的线程之前的所有线程已经进入等待状态 最后一个进入的线程执行同步状态减1后 通过nextGeneration重置同步状态更新generation满足跳出自旋的条件 并且使用signalAll唤醒所有线程后面源码分析nextGeneration 在finally中通过breakBarrier突破屏障后面讲。 nextGeneration方法如下图所示 由图可知 通过newGeneration更新generation 以满足跳出自旋的条件g ! generation。 同时通过signalAll唤醒所有线程重置同步状态。
3.2.1.3 线程等待
接下来就是CyclicBarrier的等待逻辑源码如下图所示 由图可知 通过自旋实现线程相互等待 当然需要在完成任务后跳出自旋通过generation作为突破屏障的标识 当generation发生改变时直接返回跳出自旋进入finally突破屏障 最后一个线程进入使同步状态减为0由前文可知更新generation 满足g ! generation跳出自旋 最后finally中的逻辑释锁。 4 小结
1CyclicBarrier通过ReentrantLock和Condition实现线程等待释放锁保证多线程不阻塞 2以generation作为突破屏障的标志 3线程间相互等待通过自旋实现线程间相互等待同步状态减为0时更新generation满足跳出自旋的条件最后一个线程进入后唤醒其余等待的线程为后面重用做准备并跳出当前自旋突破屏障释放锁 4突破屏障最后一个线程进入后同步状态减为0跳出自旋突破屏障 5CyclicBarrier线程可重用通过重置同步状态唤醒所有线程。