多线程之循环栅栏CyclicBarrier及原理
来源:互联网 发布:php下载系统源码 编辑:程序博客网 时间:2024/05/24 00:26
读前必看ReentrantLock——http://blog.csdn.net/qq_31957747/article/details/74929911
一、循环栅栏CyclicBarrier
CyclicBarrier它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。比如将计数器设置为10,那么凑齐第一批10个线程后,计数器就会归0,然后接着凑齐下一批10个线程,这就是循环栅栏内在的含义。
CountDownLatch和CyclicBarrier的区别:
1、 CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。2、 CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用。
构造方法:
CyclicBarrier(int parties)CyclicBarrier(int parties, Runnable barrierAction)
其中parties表示计数总数,也就是参与的线程总数。barrierAction表示当计数器一次计数完成后,系统会执行的动作。
主要方法:
int await():在所有参与线程都已经在此 barrier 上调用 await 方法之前,将一直等待。int await(long timeout, TimeUnit unit):在所有参与线程都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。
int getNumberWaiting():返回当前在屏障处等待的参与者数目。
int getParties():返回要求启动此 barrier 的参与线程数目。
boolean isBroken():查询此屏障是否处于损坏状态。
void reset():将屏障重置为其初始状态。
例子:
司令官让士兵集合完成任务的场景
public class CyclicBarrierDemo { public static class Soldier implements Runnable{ private String soldierName; private final CyclicBarrier cyclic; Soldier(CyclicBarrier cyclic,String soldierName){ this.cyclic = cyclic; this.soldierName = soldierName; } @Override public void run() { try{ //等待其他士兵到齐 cyclic.await(); doWork(); //等待所有士兵完成工作 cyclic.await(); }catch (InterruptedException e){ e.printStackTrace(); }catch (BrokenBarrierException e){ e.printStackTrace(); } } void doWork(){ try { Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println(soldierName+" :任务完成"); } } public static class BarrierRun implements Runnable{ boolean flag; int N; public BarrierRun(boolean flag,int N){ this.flag = flag; this.N = N; } @Override public void run() { if(flag){ System.out.println("司令:[士兵"+N+"个,任务完成!]"); }else{ System.out.println("司令:[士兵"+N+"个,集合完毕!]"); flag = true; } } } public static void main(String[] args) { final int N = 10; Thread[] allSoldier = new Thread[N]; boolean flag = false; CyclicBarrier cyclic = new CyclicBarrier(N,new BarrierRun(flag,N)); //设置屏障点,主要是为了执行这个方法 System.out.println("集合队伍!"); for(int i = 0;i < N;i++){ System.out.println("士兵"+i+" 报道!"); allSoldier[i] =new Thread(new Soldier(cyclic,"士兵"+i)); allSoldier[i].start(); } }}运行结果:
可以看到这段代码10个士兵线程,先后到达了2次屏障点,每次到达公共屏障点之后执行了BarrierRun的run方法,先后输出了集合完毕和任务完成。重点在于每个士兵都相互等待,直到所有的士兵一起加入,才进行下一步。
二、CyclicBarrier源码分析
与之前介绍的同步类不同,CyclicBarrier是用ReentrantLock跟Condition来实现的。
首先看他的成员变量:
private static class Generation { boolean broken = false; }private final ReentrantLock lock = new ReentrantLock();private final Condition trip = lock.newCondition();//参与线程的数量private final int parties;//所有参与线程到达屏障点后执行的动作private final Runnable barrierCommand;//当前的generation,同一批线程属于同一个generation,每跨越一次屏障点就换一个新的generationprivate Generation generation = new Generation();//还在等待的线程个数(注意:这里的等待不是condition队列中的等待线程,而是还没执行await()方法的线程,得明白每个等待的含义)private int count;
构造方法:
public CyclicBarrier(int parties, Runnable barrierAction) { if (parties <= 0) throw new IllegalArgumentException(); this.parties = parties; this.count = parties; this.barrierCommand = barrierAction; }public CyclicBarrier(int parties) { this(parties, null);}
public int await() throws InterruptedException, BrokenBarrierException { try { return dowait(false, 0L); } catch (TimeoutException toe) { throw new Error(toe); // cannot happen; }}
await()方法调用了dowait()方法:
private int dowait(boolean timed, long nanos) throws InterruptedException, BrokenBarrierException, TimeoutException { final ReentrantLock lock = this.lock; lock.lock(); //拿到lock锁 try { final Generation g = generation; if (g.broken) throw new BrokenBarrierException(); //如果线程被中断,就通过breakBarrier()破坏掉当前的generation,并唤醒所有的在condition队列中等待的线程 if (Thread.interrupted()) { breakBarrier(); throw new InterruptedException(); } //还在等待的线程数-1 int index = --count; if (index == 0) { //所有线程均调用了await(),即到达公共屏障点 boolean ranAction = false; //用来判断到达屏障点之后执行的动作是否正常执行 try { final Runnable command = barrierCommand; if (command != null) command.run();//注意这里是run()而不是start(),也就是说是同步 ranAction = true; nextGeneration();//一次屏障跨越,”更新换代”,即唤醒所有的condition队列中的线程,并把count置为parties,然后换一个新的generation return 0; } finally { if (!ranAction) breakBarrier(); } } // loop until tripped, broken, interrupted, or timed out for (;;) { //自旋 try { if (!timed) trip.await();//将当前线程加入到condition队列,释放lock锁资源,使得下一个线程能获得锁资源进入lock块 else if (nanos > 0L) nanos = trip.awaitNanos(nanos); } catch (InterruptedException ie) { //等待过程线程被中断 if (g == generation && ! g.broken) { breakBarrier(); throw ie; } else { Thread.currentThread().interrupt(); } } // 如果“当前generation已经损坏”,则抛出异常。 if (g.broken) throw new BrokenBarrierException(); // 如果“generation已经更新换代”,则返回index。 if (g != generation) return index; //”定时等待”且时间已到,则终止cyclicBarrier,唤醒condition队列中所有的线程 if (timed && nanos <= 0L) { breakBarrier(); throw new TimeoutException(); } } } finally { //释放lock锁 lock.unlock(); }}
再看上面代码提到的breakBarrier()和nextGeneration()方法:
private void breakBarrier() { generation.broken = true; //设置当前generation被破坏 count = parties; //重置count trip.signalAll(); //唤醒condition队列中的所有等待线程 }
private void nextGeneration() { trip.signalAll(); //唤醒condition队列中的所有等待线程 count = parties; //重置count generation = new Generation(); //”更新换代” }
总结:
CyclicBarrier的关键在于利用ReentrantLock和Condition,每个调用await()方法的线程都被加入到condition队列中进行等待,所有参与线程都调用了await()之后,执行设置的后续动作barrierCommand,再唤醒condition队列中的所有等待线程,重置count,并"更新换代"。
阅读全文
0 0
- 多线程之循环栅栏CyclicBarrier及原理
- java多线程之闭锁(Latch)和栅栏(CyclicBarrier)
- java多线程九 栅栏CyclicBarrier
- JDK并发包---(10)循环栅栏:CyclicBarrier
- 倒计数器:CountDownLatch | 循环栅栏:CyclicBarrier
- 栅栏CyclicBarrier
- CyclicBarrier 栅栏
- java多线程并发系列之闭锁(Latch)和栅栏(CyclicBarrier)
- java多线程并发系列之闭锁(Latch)和栅栏(CyclicBarrier)
- java多线程并发系列之闭锁(Latch)和栅栏(CyclicBarrier)
- Java并发编程之栅栏(CyclicBarrier)详解
- java多线程 栅栏(CyclicBarrier) 和 多线程辅助类 CountDownLatch
- 非常有用的并发控制-循环栅栏CyclicBarrier
- 多线程循环技术锁存器---CyclicBarrier
- Java多线程之CyclicBarrier
- Java多线程之CyclicBarrier
- JAVA多线程之CyclicBarrier
- Java多线程之CyclicBarrier
- oracle数据库字符格式修改
- python学习之模块
- Web前端知识体系精简
- PAT a1008题解
- 数据挖掘概念与技术-阅读笔记1
- 多线程之循环栅栏CyclicBarrier及原理
- PAT a 1009题解
- PMP 第4章 项目整合管理
- FFmpeg学习笔记-新旧API替换
- Rust : range,&[],vec,array中元素的类别
- 安卓NDK开发之快速熟悉JNI参数的回调
- 3、成员内部类的一个小题目(Outer.this)
- kali切换字符界面模式和切换图形界面模式
- [NOIp复习计划]:构造