Java并发之CyclicBarrier

    上一篇我们介绍了CountDownLatch的使用方法和实现原理,CountDownLatch的计数器只能使用一次,用来使一组线程阻塞于另一组线程。本篇我将介绍Java另一款并发工具CyclicBarrier。

一、同步屏障CyclicBarrier

    CyclicBarrier的字面意思是可循环使用的屏障。它的功能是,让一组线程到达一个屏障时被阻塞,知道最后一个线程到达屏障时,取消阻塞,所有被阻塞的线程才可以继续运行。

    对于失败的同步尝试,CyclicBarrier 使用了一种要么全部要么全不 (all-or-none) 的破坏模式:如果因为中断、失败或者超时等原因,导致线程过早地离开了屏障点,那么在该屏障点等待的其他所有线程也将通过BrokenBarrierException(如果它们几乎同时被中断,则用InterruptedException)以反常的方式离开。

    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使用简例:

public class Main {
	static CyclicBarrier cy = new CyclicBarrier(2,new Runnable(){
		@Override
		public void run() {
			System.out.println("end barrier !"+Thread.currentThread().getName());
		}	
	});
	public static void main(String[]args){
		Thread a = new Thread(new Runnable(){
			public void run() {		
				try {
					System.out.println("a run");
					Thread.currentThread().sleep(1000);
					System.out.println("a meet barrier");
					cy.await();
					System.out.println("a end barrier");
				} catch (Exception e) {}
			}
		},"a");
		Thread b = new Thread(new Runnable(){
			public void run() {		
				try {
					System.out.println("b run");
					Thread.currentThread().sleep(3000);
					System.out.println("b meet barrier");
					cy.await();
					System.out.println("b end barrier");
				} catch (Exception e) {}
			}
		},"b");
		Thread c = new Thread(new Runnable(){
			public void run() {		
				try {
					System.out.println("c run");
					Thread.currentThread().sleep(3000);
					System.out.println("c meet barrier");
					cy.await();
					System.out.println("c end barrier");
				} catch (Exception e) {}
			}
		},"c");
		Thread d = new Thread(new Runnable(){
			public void run() {		
				try {
					System.out.println("d run");
					Thread.currentThread().sleep(3000);
					System.out.println("d meet barrier");
					cy.await();
					System.out.println("d end barrier");
				} catch (Exception e) {}
			}
		},"d");
		a.start();
		b.start();
		try {
			Thread.currentThread().sleep(5000);
		} catch (InterruptedException e) {}
		cy.reset();
		c.start();
		d.start();
	}
}/*output:a run
b run
a meet barrier
b meet barrier
end barrier !b
b end barrier
a end barrier
d run
c run
d meet barrier
c meet barrier
end barrier !c
c end barrier
d end barrier
*/

二、CyclicBarrier实现原理

    CyclicBarrier结构类图

    由结构类图可知,CyclicBarrier中包含了一个ReentrantLock和一个Condition对象,它是通过独占锁实现的。

    构造函数

    CyclicBarrier共有两个构造函数,CyclicBarrier(int parties)和CyclicBarrier(int parties, Runnable barrierAction)。源码如下:
    public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;			//设置必须到达的线程数
        this.count = parties;			//还需要到达的线程的数量
        this.barrierCommand = barrierAction;	//当最后一个线程到达时,在解除屏障前,执行的方法
    }

    等待函数

    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;		//获取generation对象

            if (g.broken)				//如果锁已损坏,
                throw new BrokenBarrierException();	//抛出异常

            if (Thread.interrupted()) {			//如果当前线程被中断,则屏障被终止,唤醒所有线程
                breakBarrier();
                throw new InterruptedException();
            }

            int index = --count;			//等待计数 -1
            if (index == 0) {  // tripped		//到达线程数足够
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;	//执行销毁前动作
                    if (command != null)
                        command.run();
                    ranAction = true;
                    nextGeneration();			//唤醒所有等待线程 并更新generation
                    return 0;
                } finally {
                    if (!ranAction)			//抛出异常 执行动作失败 则同样终止屏障
                        breakBarrier();
                }
            }

            // loop until tripped, broken, interrupted, or timed out
            for (;;) {					//当前阻塞线程数不够 
                try {
                    if (!timed)				//如果不是超时等待
                        trip.await();			//当前线程阻塞在 condition trip下
                    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();
                    }
                }

                if (g.broken)
                    throw new BrokenBarrierException();

                if (g != generation)		//屏障已换代
                    return index;		//返回计数

                if (timed && nanos <= 0L) {	//如果是超时等待并且时间已到 则终止屏障
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();			//释放锁
        }
    }

    dowait的作用就是让当前线程阻塞,直到有parties个线程到达barrier、当前线程被中断、超时这三种情况时,当前线程才继续执行。

    1.generation是CyclicBarrier中的一个成员。

private Generation generation = new Generation();

private static class Generation {
    boolean broken = false;
}


    同一批线程属于同一代,即同一个generation;CyclicBarrier通过内置generation对象,记录属于哪一代。每次屏障解除,都会换代。

    2.如果当前线程中断,则通过breakBarrier来终止CyclicBarrier

    private void breakBarrier() {
        generation.broken = true;
        count = parties;
        trip.signalAll();
    }

    它会调用condition的signalAll来唤醒所有线程。

    3.nextGeneration()方法会创建新的一代,并且唤醒所有上一代线程。

   private void nextGeneration() {
        // signal completion of last generation
        trip.signalAll();
        // set up next generation
        count = parties;
        generation = new Generation();
    }





猜你喜欢

转载自blog.csdn.net/u010771890/article/details/74940986