Java多线程系列(八)—CyclicBarrier源码分析

Java多线程系列(八)—CyclicBarrier源码分析

CyclicBarrier是一个同步辅助类,允许一组相关线程相互等待,直到所有线程都到达某个公共屏障点;因为该屏障点在释放后可以循环重用,所以称为循环屏障;

个人主页:tuzhenyu’s page
原文地址:Java多线程系列(八)—CyclicBarrier源码分析

(0) CyclicBarrier循环屏障实例

class MyThread extends Thread{
    private CyclicBarrier cb;
    public MyThread(String name,CyclicBarrier cb){
        super(name);
        this.cb = cb;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+" is going to await");
        try {
            cb.await();
            System.out.println(Thread.currentThread().getName()+" is going to continue");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
public class CyclicBarrierDemo {
    public static void main(String[] args) throws Exception{
        CyclicBarrier cb = new CyclicBarrier(3, new Runnable() {
            @Override
            public void run() {
                System.out.println("barrier action");
            }
        });
        MyThread t1 = new MyThread("t1",cb);
        MyThread t2 = new MyThread("t2",cb);
        t1.start();
        t2.start();
        System.out.println("main is going to await");
        cb.await();
        System.out.println("main is going to continue");
    }
}

运行结果

t1 is going to await

main is going to await

t2 is going to await

barrier action

t1 is going to continue

main is going to continue

t2 is going to continue

(1)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()

将屏障重置为其初始状态。

(2)CyclicBarrier循环屏障的语义实现

1. CyclicBarrier类的结构

  • CyclicBarrier类的功能实现主要依赖于ReentrantLock互斥锁和Condition类

这里写图片描述

1. CyclicBarrier类的构造函数

  • 只指定屏障数目,不指定屏障结束后运行的线程
public CyclicBarrier(int parties) {
    this(parties, null);
}
  • 指定屏障数目和结束后运行的线程
public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties;
    this.barrierCommand = barrierAction;
}

2. CyclicBarrier的await()循环等待实现

  • await()循环等待的实现流程

这里写图片描述

  • CyclicBarrier实例的await()方法是通过dowait()方法实现
public int await() throws InterruptedException, BrokenBarrierException {
    try {
        return dowait(false, 0L);
    } catch (TimeoutException toe) {
        throw new Error(toe); // cannot happen
    }
}
  • dowait()方法实现了循环等待的语义

    • 对dowait()方法lock()加锁防止多个线程同时调用

    • 判断屏障是否损坏,如果损坏抛出错误

    • 判断当前线程是否被中断,如果被中断则抛出错误

    • 判断需要进入屏障等待的线程数目是否为0

      • 如果不是,则将当前线程阻塞放入Condition队列

      • 如果是,则先执行屏障绑定先线程的run()方法,然后调用nextGeneration()方法将Condition队列中的阻塞的线程转移到同步队列中;

    • 执行完await()方法后调用unlock()解锁,并从同步对列中唤醒一个线程(该线程可能会立即执行也可能不立即执行取于CPU的调度);每一个执行完unlock()方法的线程都会从同步队列中唤醒一个阻塞的线程;

这里写图片描述

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();

    }

}
  • 调用nextGeneration()方法将Condition队列中的阻塞线程转移到同步队列中,将count重置实现屏障的循环;
private void nextGeneration() {
    trip.signalAll();
    count = parties;
    generation = new Generation();
}
  • 调用breakBarrier()方法损坏当前线程,将Condition队列中的线程转移到同步队列中;
private void breakBarrier() {

        // 设置状态

        generation.broken = true;

        // 恢复正在等待进入屏障的线程数量

        count = parties;

        // 唤醒所有线程

        trip.signalAll();

    }

总结

  • CyclicBarrier主要作用是实现循环屏障,也就是说特定功数量的线程必须都运行到特定的点,所有线程才会被唤醒继续执行;语义的实现主要通过await()方法的调用;

  • CyclicBarrier语义的实现主要是通过ReentrantLock和其对应的Condition,在到达屏障的数目小于要求数目时会将线程阻塞放入Condition队列;当到达屏障的数目达到要求的数目时会将Condition队列中的线程转移到ReentrantLock的同步队列中;在执行unlock()方法时会唤醒同步队列头的线程;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值