JUC锁-10之 CyclicBarrier原理和示例

来源:互联网 发布:maxwell软件渲染 编辑:程序博客网 时间:2024/06/03 20:08

0.本文目录

  • 本文目录
  • 开篇明志
  • CyclicBarrier简介
  • CyclicBarrier函数列表
  • CyclicBarrier数据结构
  • CyclicBarrier源码分析基于JDK170_40
    • 构造函数
    • 等待函数
  • CyclicBarrier的使用示例

1.开篇明志

本章介绍JUC包中的CyclicBarrier锁。

2.CyclicBarrier简介

CyclicBarrier是一个同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。

注意比较CountDownLatchCyclicBarrier

  • CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。
  • CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。

3.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()//将屏障重置为其初始状态。

4.CyclicBarrier数据结构

CyclicBarrier的UML类图如下:
这里写图片描述
CyclicBarrier是包含了”ReentrantLock对象lock”和”Condition对象trip”,它是通过独占锁实现的。下面通过源码去分析到底是如何实现的。

5.CyclicBarrier源码分析(基于JDK1.7.0_40)

CyclicBarrier.java 点击这里

CyclicBarrier是通过ReentrantLock(独占锁)Condition来实现的。

下面,我们分析CyclicBarrier中3个核心函数: 构造函数, await()作出分析。

1. 构造函数

CyclicBarrier的构造函数共2个:CyclicBarrier 和 CyclicBarrier(int parties, Runnable barrierAction)。第1个构造函数是调用第2个构造函数来实现的,下面第2个构造函数的源码。

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者之一发生,当前线程才继续执行。

  • generation是CyclicBarrier的一个成员遍历,它的定义如下:
private Generation generation = new Generation();private static class Generation {    boolean broken = false;}

在CyclicBarrier中,同一批的线程属于同一代,即同一个Generation;CyclicBarrier中通过generation对象,记录属于哪一代。
当有parties个线程到达barrier,generation就会被更新换代。

  • 如果当前线程被中断,即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上所有的等待线程。

  • 将“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的值。

  • 在for(;;)循环中。timed是用来表示当前是不是“超时等待”线程。如果不是,则通过trip.await()进行等待;否则,调用awaitNanos()进行超时等待。

5.CyclicBarrier的使用示例

示例1
新建5个线程,这5个线程达到一定的条件时,它们才继续往后运行。

import java.util.concurrent.CyclicBarrier;import java.util.concurrent.BrokenBarrierException;public class CyclicBarrierTest1 {    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();            }        }    }}

运行结果:

Thread-1 wait for CyclicBarrier.Thread-2 wait for CyclicBarrier.Thread-3 wait for CyclicBarrier.Thread-4 wait for CyclicBarrier.Thread-0 wait for CyclicBarrier.Thread-0 continued.Thread-4 continued.Thread-2 continued.Thread-3 continued.Thread-1 continued.

结果说明:主线程中新建了5个线程,所有的这些线程都调用cb.await()等待。所有这些线程一直等待,直到cb中所有线程都达到barrier时,这些线程才继续运行!

示例2

新建5个线程,当这5个线程达到一定的条件时,执行某项任务。

import java.util.concurrent.CyclicBarrier;import java.util.concurrent.BrokenBarrierException;public class CyclicBarrierTest2 {    private static int SIZE = 5;    private static CyclicBarrier cb;    public static void main(String[] args) {        cb = new CyclicBarrier(SIZE, new Runnable () {            public void run() {                System.out.println("CyclicBarrier's parties is: "+ cb.getParties());            }        });        // 新建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();            }        }    }}

运行结果:

Thread-1 wait for CyclicBarrier.Thread-2 wait for CyclicBarrier.Thread-3 wait for CyclicBarrier.Thread-4 wait for CyclicBarrier.Thread-0 wait for CyclicBarrier.CyclicBarrier's parties is: 5Thread-0 continued.Thread-4 continued.Thread-2 continued.Thread-3 continued.Thread-1 continued.

结果说明:主线程中新建了5个线程,所有的这些线程都调用cb.await()等待。所有这些线程一直等待,直到cb中所有线程都达到barrier时,执行新建cb时注册的Runnable任务。

阅读全文
0 0
原创粉丝点击