线程屏障CyclicBarrier实现原理
程序员文章站
2023-11-03 12:36:22
生产环境中,存在需要等待多个线程都达到某种状态后,才继续运行的情景。并发工具CyclicBarrier就能够完成这种功能。本篇从源码方面,简要分析CyclicBarrier的实现原理。 使用示例 执行结果如下: 可以看到线程1,2,3在同一个时间结束。 源码分析 主要成员: CyclicBarrie ......
生产环境中,存在需要等待多个线程都达到某种状态后,才继续运行的情景。并发工具cyclicbarrier就能够完成这种功能。本篇从源码方面,简要分析cyclicbarrier的实现原理。
使用示例
public class cyclicbarriertest { public static void main(string[] args) { //屏障,阻拦3个线程 cyclicbarrier cyclicbarrier = new cyclicbarrier(3); new thread(new runnable() { @override public void run() { system.out.println("线程1正在执行"); try { // 等待 cyclicbarrier.await(); } catch (exception e) { e.printstacktrace(); } system.out.println("线程1运行结束,时间: " + system.currenttimemillis()); } }).start(); new thread(new runnable() { @override public void run() { system.out.println("线程2正在执行"); try { // 等待 cyclicbarrier.await(); } catch (exception e) { e.printstacktrace(); } system.out.println("线程2运行结束,时间: " + system.currenttimemillis()); } }).start(); new thread(new runnable() { @override public void run() { system.out.println("线程3正在执行"); try { //线程3阻塞2秒,测试效果 thread.sleep(2000); // 等待 cyclicbarrier.await(); } catch (exception e) { e.printstacktrace(); } system.out.println("线程3运行结束,时间: " + system.currenttimemillis()); } }).start(); } }
执行结果如下:
线程1正在执行 线程2正在执行 线程3正在执行 线程1运行结束,时间: 1550324116837 线程3运行结束,时间: 1550324116837 线程2运行结束,时间: 1550324116837
可以看到线程1,2,3在同一个时间结束。
源码分析
主要成员:
private final reentrantlock lock = new reentrantlock(); private final condition trip = lock.newcondition(); private int count;
cyclicbarrier主要借助重入锁reentrantlock和condition实现。count初始值等于cyclicbarrier实例化指明的等待线程数量,用于等待线程计数。
主要方法await()
public int await() throws interruptedexception, brokenbarrierexception { try { return dowait(false, 0l); } catch (timeoutexception toe) { throw new error(toe); // cannot happen } } private int dowait(boolean timed, long nanos) throws interruptedexception, brokenbarrierexception, timeoutexception { final reentrantlock lock = this.lock; lock.lock(); // 1 try { final generation g = generation; if (g.broken) throw new brokenbarrierexception(); if (thread.interrupted()) { breakbarrier(); throw new interruptedexception(); } int index = --count; // 2 if (index == 0) { // 3 boolean ranaction = false; try { final runnable command = barriercommand; if (command != null) command.run(); ranaction = true; nextgeneration(); // 4 return 0; } finally { if (!ranaction) breakbarrier(); // 5 } } // loop until tripped, broken, interrupted, or timed out for (;;) { try { if (!timed) trip.await(); // 6 else if (nanos > 0l) nanos = trip.awaitnanos(nanos); } catch (interruptedexception ie) { if (g == generation && ! g.broken) { breakbarrier(); throw ie; } else { // we're about to finish waiting even if we had not // been interrupted, so this interrupt is deemed to // "belong" to subsequent execution. thread.currentthread().interrupt(); } } if (g.broken) throw new brokenbarrierexception(); if (g != generation) return index; if (timed && nanos <= 0l) { breakbarrier(); throw new timeoutexception(); } } } finally { lock.unlock(); // 7 } }
- 对当前对象加锁
- 每个线程获得锁,执行这部分代码时,都把count - 1,记做index
- 如果index为0,执行第4步,代表cyclicbarrier屏障已经拦截了足够数量(count)的线程,线程可以接着往下执行了。不为0,说明当前线程还没有达到屏障cyclicbarrier拦截的数量,执行第6步
- 调用nextgeneration()方法,唤醒所有等待线程
- breakbarrier()确保一定能执行唤醒动作
- 调用condition的await()方法,将当前线程放入等待队列,释放锁
- 一定执行的释放锁动作。
nextgeneration()的代码如下:
private void nextgeneration() { // signal completion of last generation trip.signalall(); // set up next generation count = parties; generation = new generation(); }
使用condition的signalall()方法,唤醒全部等待线程
说完cyclicbarrier的原理之后,再对本篇的使用示例做一下描述:
- 线程1开始执行,调用await()方法,获得锁。此时count为3,count--,故count为2,index为2,调用condition.await()方法,线程1进入等待队列,释放锁
- 线程2开始执行,过程与第一步相同,只是count减为1
- 线程3开始执行,获得锁,count减为0,达到拦截数量,调用nextgeneration()方法唤醒全部线程,释放自己持有的锁
- 线程1,2都被唤醒,根据锁竞争结果,依次执行完await()方法,最后释放锁
- 3个线程再往下执行自己的run()方法