多线程编程-- part 8 CyclicBarrier
Posted jiguojing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程编程-- part 8 CyclicBarrier相关的知识,希望对你有一定的参考价值。
CyclicBarrier简介
cuclicBarrier允许一组线程互相等待,直到到达某个公共屏障点(common barrier point)。因为该barrier在释放等待线程后可以重用,所以称它为循环的barrier。
CyclicBarrier函数列表
CyclicBarrier(int parties) 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在启动 barrier 时执行预定义的操作。 CyclicBarrier(int parties, Runnable barrierAction) 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行。 int await() 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。 int await(long timeout, TimeUnit unit) 在所有参与者都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。 int getNumberWaiting() 返回当前在屏障处等待的参与者数目。 int getParties() 返回要求启动此 barrier 的参与者数目。 boolean isBroken() 查询此屏障是否处于损坏状态。 void reset() 将屏障重置为其初始状态。
CyclicBarrier数据结构
可见其包含的对象:
(1)parties:定义多少个等待线程可以启动屏障
(2)count:处于等待状态的线程数量
(3)lock:是ReentrantLock独占锁
(4)trip:condition条件控制,控制线程的等待和激活
(5)barrierCommand:表示当parties个处于等待状态的线程到达屏障时,要执行的动作
(6)generation:记录线程属于那一代,当有parties个线程到达barrier时,generation会被换代
CyclicBarrier核心函数
1.构造函数
(1)CyclicBarrier(int parties), CyclicBarrier(int parties, Runnable barrierAction)
public CyclicBarrier(int parties, Runnable barrierAction) { if (parties <= 0) throw new IllegalArgumentException(); // parties表示“必须同时到达barrier的线程个数”。 this.parties = parties; // count表示“处在等待状态的线程个数”。 this.count = parties; // barrierCommand表示“parties个线程到达barrier时,会执行的动作”。 this.barrierCommand = barrierAction; }
(2)等待函数
CyclicBarrier.java中await()方法如下:
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 { // 保存“当前的generation” final Generation g = generation; // 若“当前generation已损坏”,则抛出异常。 if (g.broken) throw new BrokenBarrierException(); // 如果当前线程被中断,则通过breakBarrier()终止CyclicBarrier,唤醒CyclicBarrier中所有等待线程。 if (Thread.interrupted()) { breakBarrier(); throw new InterruptedException(); } // 将“count计数器”-1 int index = --count; // 如果index=0,则意味着“有parties个线程到达barrier”。 if (index == 0) { // tripped boolean ranAction = false; try { // 如果barrierCommand不为null,则执行该动作。 final Runnable command = barrierCommand; if (command != null) command.run(); ranAction = true; // 唤醒所有等待线程,并更新generation。 nextGeneration(); return 0; } finally { if (!ranAction) breakBarrier(); } } // 当前线程一直阻塞,直到“有parties个线程到达barrier” 或 “当前线程被中断” 或 “超时”这3者之一发生, // 当前线程才继续执行。 for (;;) { try { // 如果不是“超时等待”,则调用awati()进行等待;否则,调用awaitNanos()进行等待。 if (!timed) trip.await(); 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; // 如果是“超时等待”,并且时间已到,则通过breakBarrier()终止CyclicBarrier,唤醒CyclicBarrier中所有等待线程。 if (timed && nanos <= 0L) { breakBarrier(); throw new TimeoutException(); } } } finally { // 释放“独占锁(lock)” lock.unlock(); } }
说明:dowait()的作用就是让当前线程阻塞,直到“有parties个线程到达barrier” 或 “当前线程被中断” 或 “超时”这3者之一发生,当前线程才继续执行。
(01) generation是CyclicBarrier的一个成员遍历,它的定义如下:
private Generation generation = new Generation(); private static class Generation { boolean broken = false; }
在CyclicBarrier中,同一批的线程属于同一代,即同一个Generation;CyclicBarrier中通过generation对象,记录属于哪一代。
当有parties个线程到达barrier,generation就会被更新换代。
(02) 如果当前线程被中断,即Thread.interrupted()为true;则通过breakBarrier()终止CyclicBarrier。breakBarrier()的源码如下:
private void breakBarrier() { generation.broken = true; count = parties; trip.signalAll(); }
breakBarrier()会设置当前中断标记broken为true,意味着“将该Generation中断”;同时,设置count=parties,即重新初始化count;最后,通过signalAll()唤醒CyclicBarrier上所有的等待线程。
(03) 将“count计数器”-1,即--count;然后判断是不是“有parties个线程到达barrier”,即index是不是为0。
当index=0时,如果barrierCommand不为null,则执行该barrierCommand,barrierCommand就是我们创建CyclicBarrier时,传入的Runnable对象。然后,调用nextGeneration()进行换代工作,nextGeneration()的源码如下:
private void nextGeneration() { trip.signalAll(); count = parties; generation = new Generation(); }
首先,它会调用signalAll()唤醒CyclicBarrier上所有的等待线程;接着,重新初始化count;最后,更新generation的值。
(04) 在for(;;)循环中。timed是用来表示当前是不是“超时等待”线程。如果不是,则通过trip.await()进行等待;否则,调用awaitNanos()进行超时等待
CyclicBarrier示例
(1)新建5个线程,都调用await()等待,这些线程都到达屏障,继续往后执行
public class testHello { private static int SIZE = 5; private static CyclicBarrier cb; public static void main(String[] args) { cb = new CyclicBarrier(SIZE); // 新建5个任务 for(int i=0; i<SIZE; i++) new InnerThread().start(); } static class InnerThread extends Thread{ public void run() { try { System.out.println(Thread.currentThread().getName() + " wait for CyclicBarrier."); // 将cb的参与者数量加1 cb.await(); // cb的参与者数量等于5时,才继续往后执行 System.out.println(Thread.currentThread().getName() + " continued."); } catch (BrokenBarrierException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
(2)新建5个线程,都调用await()等待,这些线程都到达屏障,执行runnable中定义的任务
public class testHello { private static int SIZE = 5; private static CyclicBarrier cb; public static void main(String[] args) { cb = new CyclicBarrier(SIZE, new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + " runnable start"); } }); // 新建5个任务 for(int i=0; i<SIZE; i++) new InnerThread().start(); } static class InnerThread extends Thread{ public void run() { try { System.out.println(Thread.currentThread().getName() + " wait for CyclicBarrier."); // 将cb的参与者数量加1 cb.await(); // cb的参与者数量等于5时,才继续往后执行 System.out.println(Thread.currentThread().getName() + " continued."); } catch (BrokenBarrierException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
可以看出当parties个线程到达屏障时,会执行屏障的Runable定义的任务
以上是关于多线程编程-- part 8 CyclicBarrier的主要内容,如果未能解决你的问题,请参考以下文章
多线程编程-- part5.1 互斥锁ReentrantLock