并发源码解析(六)--CyclicBarrier

一、CyclicBarrier

CyclicBarrier–回环屏障:有点类似于CountDownLatch,但是比CountDownLatch更厉害一点。CountDownLatch是一次性的计数器,计数到0就会立刻返回。而CyclicBarrier的计数器达到0了之后可以重用。

二、源码分析

2.1 继承关系

2.2 属性

   /** 可以看出基于独占锁实现 */
    private final ReentrantLock lock = new ReentrantLock();
    /** 条件变量 */
    private final Condition trip = lock.newCondition();
    /** 纪录总的线程个数,这个值不会改变的 */
    private final int parties;
    /* 冲破阻塞点时执行的任务 */
    private final Runnable barrierCommand;
    /**当前代 */
    private Generation generation = new Generation();

    /**
     * 计数器,count为0代表所有线程达到屏障点
     */
    private int count;

还有一个静态内部类,只有一个boolean变量,用来纪录当前屏障是否被打破

private static class Generation {
    boolean broken = false;
}

2.3 构造方法

public CyclicBarrier(int parties) {
	//这个构造方法使用了两个参数的构造方法
    this(parties, null);
}

public CyclicBarrier(int parties, Runnable barrierAction) {
	//传入的int值小于0,则抛异常
    if (parties <= 0) throw new IllegalArgumentException();
    //赋值,此时parties和count相等。后面的计数操作都是改变count
    this.parties = parties;
    this.count = parties;
    //传入的任务
    this.barrierCommand = barrierAction;
}

2.4 常用方法

2.4.1 await()方法

public int await() throws InterruptedException, BrokenBarrierException {
    try {
    	//调用了dowait方法
        return dowait(false, 0L);
    } catch (TimeoutException toe) {
        throw new Error(toe); // cannot happen
    }
}
//传入参数false,0L
private int dowait(boolean timed, long nanos)
    throws InterruptedException, BrokenBarrierException,
           TimeoutException {
    //上锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        final Generation g = generation;
		//检查屏障是否被打破,抛出异常
        if (g.broken)
            throw new BrokenBarrierException();
		
		//线程是否被打断
        if (Thread.interrupted()) {
            breakBarrier();
			//抛出异常
            throw new InterruptedException();
        }
		
		//index==0表示所有线程达到屏障点,执行初始化时传递的任务
        int index = --count;
        //如果是0
        if (index == 0) {  // tripped
            boolean ranAction = false;
            try {
            	//执行构造时传递的任务,开始执行
                final Runnable command = barrierCommand;
                if (command != null)
                    command.run();
                ranAction = true;
                //唤醒所有线程,开始新的一代
                nextGeneration();
                return 0;
            } finally {
                if (!ranAction)
                    breakBarrier();
            }
        }

        //计数器不为0则执行此循环
        for (;;) {
            try {
            	//传入的是false,非定时模式
                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();
                }
            }
			
			//屏障被打破,抛异常
            if (g.broken)
                throw new BrokenBarrierException();
			//g已换代,返回线程数
            if (g != generation)
                return index;
			//超时,打破屏障,唤醒所有线程
            if (timed && nanos <= 0L) {
                breakBarrier();
                throw new TimeoutException();
            }
        }
    } finally {
    	//释放屏障
        lock.unlock();
    }
}

下一代

private void nextGeneration() {
    // 唤醒条件队列所有的线程
    trip.signalAll();
    // 重新设置计数器
    count = parties;
    //设置新的generation
    generation = new Generation();
}

打破屏障

private void breakBarrier() {
	//打破标志
    generation.broken = true;
    //计数器置为parties
    count = parties;
    //唤醒所有线程
    trip.signalAll();
}

CyclicBarrier的方法基本上就这些了

三、总结

当一个线程使用dowait()方法时,首先获取独占锁。然后将创建CyclicBarrier时传入的参数-1,此时后面的线程全部阻塞。-1后如果不等0,则当前线程进入条件队列阻塞挂起,并释放锁。其他线程则争夺锁,直到使用dowait方法重复上述过程

当计数器==0时,最后一个线程会执行创建时传入的任务(如果有),然后唤醒所有的线程,并充值CyclicBarrier,继续运行

做个示例:

package com.cf.test.lock;

import java.util.concurrent.*;

public class TestDemo3 {

    static int count = 1;

    public static void main(String[] args) throws InterruptedException {


        CyclicBarrier barrier = new CyclicBarrier(2, () -> {
            System.out.println("关卡任务完成了");
        });

        ExecutorService service = Executors.newFixedThreadPool(2);

        service.submit(() -> {
            try {
                System.out.println("任务1-1完成");
                barrier.await();
                System.out.println("任务2-1完成");
                barrier.await();
                System.out.println("任务3-1完成");
                barrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        });

        service.submit(() -> {
            try {
                System.out.println("任务1-2完成");
                barrier.await();
                System.out.println("任务2-2完成");
                barrier.await();
                System.out.println("任务3-1完成");
                barrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        });


        service.shutdown();
        if(service.awaitTermination(1, TimeUnit.MINUTES)){
            System.out.println("已通关");
        }

    }

}

结果:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值