实战java高并发程序设计之CyclicBarrier源码分析

版权声明:转载注明出处 https://blog.csdn.net/nobody_1/article/details/83021048

前面一篇博客介绍了CountDownLatch并发类,从源码的角度理解它。这篇博客讲讲另外一个并发类:CyclicBarrier!

使用场景

CyclicBarrier字面意思是循环栅栏,即这个栅栏可以循环使用。栅栏可以理解为挡住多个线程运行的挡板,即声明一个CyclicBarrier对象表示栅栏,把栅栏放到每个线程内部,线程调用栅栏方法的时刻就是线程等待的时刻。具体过程如下:
栅栏
CyclicBarrier为多个线程设置了一个栅栏挡板,当各个线程运行到该栅栏挡板时,当且仅当线程都到达这个栅栏挡板时,各个线程才会继续执行,否则就需要等待其他线程运行到该栅栏挡板处。就好比,三个人约定做一件事,每个人的效率不一样,导致完成的时刻不一样,同时,当三个人都做完后就交由某个人检查,随后三个人可以各自活动。

代码实例

定义三个person类线程,构造线程的时候注入一个共同的栅栏,每个线程完成各自的工作,在调用await()之前用sleep()模拟任务,当线程执行到await()时会判断其他线程是否执行到await(),当判断当前线程为最后一个到达栅栏时,则由当前线程执行栅栏里的任务(action),然后各线程执行各自剩余工作(睡觉或者吃饭或者打豆豆);如果判断当前线程不是最后一个到达栅栏的线程,则会等待;

public class CyclicBarrierTest {

	public static void main(String[] args){
         // cb表示栅栏 3代表线程数  Runable对象表示线程都到达栅栏后执行的任务
		CyclicBarrier cb = new CyclicBarrier(3, new Runnable() {   
			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName() + " :all is end!");
			}
		});
		// 线程1
		Thread PersonOne = new Thread(new PersonOne(cb),"PersonOne");
		PersonOne.start();
		// 线程2
		Thread PersonTwo = new Thread(new PersonTwo(cb),"PersonTwo");
		PersonTwo.start();
		// 线程3
		Thread PersonThree = new Thread(new PersonThree(cb),"PersonThree");
		PersonThree.start();
	}
}
class PersonOne implements Runnable{
	private CyclicBarrier cyclicBarrier;
	public PersonOne(CyclicBarrier cyclicBarrier) {
		this.cyclicBarrier = cyclicBarrier;
	}
	
	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + " :PersonOne arriving!");
		try {
			Thread.sleep(2000);    // 睡眠模拟完成的工作
			System.out.println(Thread.currentThread().getName() + " :PersonOne does!");
			cyclicBarrier.await();   // 判断是否最后一个到达栅栏
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (BrokenBarrierException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " :PersonOne going sleep!");   // 睡觉
	}
}

class PersonTwo implements Runnable{
	private CyclicBarrier cyclicBarrier;
	public PersonTwo(CyclicBarrier cyclicBarrier) {
		this.cyclicBarrier = cyclicBarrier;
	}
	
	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + " :PersonTwo arriving!");
		try {
			Thread.sleep(2000);   // 睡眠模拟完成的工作
			System.out.println(Thread.currentThread().getName() + " :PersonTwo does!");
			cyclicBarrier.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (BrokenBarrierException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " :PersonTwo eating lunch!");  // 吃饭
	}
}

class PersonThree implements Runnable{
	private CyclicBarrier cyclicBarrier;
	public PersonThree(CyclicBarrier cyclicBarrier) {
		this.cyclicBarrier = cyclicBarrier;
	}
	
	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + " :PersonThree arriving!");
		try {
			Thread.sleep(2000);   // 睡眠模拟完成的工作
			System.out.println(Thread.currentThread().getName() + " :PersonThree dose!");
			cyclicBarrier.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (BrokenBarrierException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " :PersonThree dadoudou!");  // 打豆豆
	}
}

执行结果:

PersonOne :PersonOne arriving!
PersonTwo :PersonTwo arriving!
PersonThree :PersonThree arriving!
PersonOne :PersonOne does!
PersonTwo :PersonTwo does!
PersonThree :PersonThree dose!
PersonThree :all is end!   // 栅栏任务由最后一个到达栅栏的线程执行
PersonThree :PersonThree dadoudou!
PersonOne :PersonOne going sleep!
PersonTwo :PersonTwo eating lunch!

源码分析

构造函数:
利用重载提供两个构造函数

// parties表示线程数 
public CyclicBarrier(int parties) {
        this(parties, null);   // 调用两个参数的构造方法,即下面的构造方法
    }
// parties表示线程数 barrierAction表示栅栏任务,即最后一个线程到达栅栏后执行的任务
public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;    // final修饰的 用于下次为count赋值 实现循环功能
        this.count = parties;     // 等待的数量,栅栏一次循环完成会把parties赋值为count
        this.barrierCommand = barrierAction;    // 栅栏任务
    }

重点是线程中调用的await()方法

// 如果当前线程在等待过程中阻塞,则抛出InterruptedException
//  BrokenBarrierException发生情况较多:①当前线程等待过程中,另外一个线程阻塞或者超时;②栅栏被重置了;③await方法在调用,栅栏被破坏;④由于一个异常导致栅栏任务执行失败
public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);  // 
        } catch (TimeoutException toe) {  // 超时异常直接抛出错误
            throw new Error(toe); // cannot happen
        }
    }

dowait()

private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;   // 利用重入锁保证线程的安全性
        lock.lock();
        try {
            final Generation g = generation;   // 栅栏每broken一次,Generation就需要重新生成一个对象
            // 如果当前栅栏broken,则抛出BrokenBarrierException异常;
            if (g.broken)
                throw new BrokenBarrierException();
            // 如果当前线程被阻塞,则栅栏被broken,抛出InterruptedException异常
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
            // 等待的线程数(count)减1
            int index = --count;
            if (index == 0) {  // tripped 若等待线程数为0,即该线程为最后一个到达栅栏的线程
                boolean ranAction = false;
                try {
                    // 当前线程执行栅栏定义的任务
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    ranAction = true;
                    nextGeneration();    // 当前栅栏任务完成栅栏功能耗尽,重新定义一个栅栏
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier();
                }
            }
            // 如果不是最后一个到达栅栏的线程,则一直循环,直到栅栏被broken,线程被阻塞或者超时
            for (;;) {
                try {
                    if (!timed)   // timed默认为false 则继续执行
                        trip.await();   // 线程等待直到满足condition的情况(栅栏被broken/线程阻塞)
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        // We're about to finish waiting even if we had not
                        // been interrupted, so this interrupt is deemed to
                        // "belong" to subsequent execution.
                        Thread.currentThread().interrupt();
                    }
                }
                // 如果栅栏被破坏 抛出BrokenBarrierException异常
                if (g.broken)
                    throw new BrokenBarrierException();
                // 如果当前栅栏的generation对象不一致,返回等待线程数
                if (g != generation)
                    return index;
                // 此处抛出TimeOutException异常 按理来说不会执行,因为timed值为false
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();   // 释放栅栏的锁,便于其他线程调用栅栏的wati()
        }
    }

从源码分析可知:
->线程通过ReentrantLock对象同步调用栅栏对象的dowait()方法;
->通过count数计算当前等待的线程数量,若当前线程是最后一个等待线程(最后一个到达栅栏的线程),则由该线程执行栅栏任务(barrierAction);
->若当前线程不是最后一个到达栅栏的线程,则会把当前线程包装成一个满足等待条件的node,并放入等待条件队列;
->释放重入锁,便于其他线程调用栅栏的dowait()。

注意事项

与CounDownLatch的区别:
CountDownLatch是线程间的等待,是当前面N个线程全部执行完,第n+1的线程才可以执行!且CountDownLatch只能使用一次
CyclicBarrier是线程内的等待,是当N个线程都执行到一个栅栏状态时,我N个线程才会继续执行!CyclicBarrier可以循环使用

猜你喜欢

转载自blog.csdn.net/nobody_1/article/details/83021048