Java线程--CyclicBarrier循环屏障

CyclicBarrier循环屏障

目录

CyclicBarrier循环屏障

CyclicBarrier原理 

CyclicBarrier示例 

 CyclicBarrier和CountDownLatch区别


CyclicBarrier原理 

CyclicBarrier

下面是部分源码,以及源码中调用相关类的部分源码剖析: 



public class CyclicBarrier {
    
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition trip = lock.newCondition();
    private Generation generation = new Generation();
    private int count;

    public CyclicBarrier(int parties) {
	this.parties = parties;
        this.count = parties;
    }

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

    /**
     * Main barrier code, covering the various policies.
     */
    private int dowait(boolean timed, long nanos) {
       
	lock.lock();
        
	try {
            final Generation g = generation;

            int index = --count;
            if (index == 0) {		// 屏障开启,可通行
                boolean ranAction = false;
                try {
                    ranAction = true;
                    nextGeneration();   // 唤醒所有卡在barrier屏障前的线程,并恢复count使其可循环利用
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier();
                }
            }

            // loop until tripped, broken, interrupted, or timed out
            
            for (;;) {		        // index!=0 说明人还没到齐,仍然无法开启屏障
                try {
                    if (!timed)
                        trip.await();	// 当前线程压入条件队列
                } catch (InterruptedException ie) {
                }

                if (g != generation)
                    return index;
            }
        } finally {
            lock.unlock();
        }
    }

    

    public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
        }
    }

    
}

public class ConditionObject implements Condition, java.io.Serializable {

	public ConditionObject() { }
		   
	private void doSignalAll(Node first) {
		lastWaiter = firstWaiter = null;
		
		do {	//从第一个节点开始,循环处理,唤醒所有barrier屏障前的等待线程
			Node next = first.nextWaiter;
			first.nextWaiter = null;
			transferForSignal(first);
			first = next;
		} while (first != null);
	}
	
	
	public final void signalAll() {
		doSignalAll(first);
	}        
}

public abstract class AbstractQueuedSynchronizer{

	final boolean transferForSignal(Node node) {
        LockSupport.unpark(node.thread);	// 唤醒线程
        return true;
    }
}

源代码分析如下:

CyclicBarrier类的await()方法:是我们程序中经常调用的,其内部调用了Cyclic类的dowait()方法

CyclicBarrier类的dowait()方法:

获得锁:

临界区:业务逻辑,判断当前屏障能否放行

               index==0,能放行,则调用CyclicBarrier类的nextCondition()方法

               index!=0, 不能放行,当前线程压入条件队列,进行等待

释放锁:

CyclicBarrier类的nextCondition()方法:调用ConditionObject类的signalAll()方法,恢复循环计数功能count = parties;

ConditionObject类的signalAll()方法:循环处理,条件队列中的线程转入同步队列准备执行。即唤醒所有到达屏障前的等待线程


源码分析总结:

白话文阐述:众人都到达,才能推开屏障大门,得以通行。假定大门4斤,每人只能推动1斤,必须4人都到齐后,才能推开大门。4人通过大门后,大门自行关闭又形成屏障,需要下一组还得是4个人都到门前一起才能推开大门,如此循环反复....直至所有人都通过大门。

程序上来说:屏障计数量初始化为N。每一个线程到达屏障前(即当前线程运行到await()方法处),该线程被阻塞,屏障计数量N-1,直至屏障降为零(即到达屏障前的线程个数之和达到屏障计数量N),屏障才会打开,唤醒这N个到达屏障前的多线程,都可通过屏障,继续得以执行。


适应的业务场景:

两个或者多个线程的一组线程,必须在预定的执行点进行等待,直到这组线程中的所有线程都到达执行点为止。举例:景区观光小车,人不坐满不发车。在车子想开走观光之前(执行点),一车人(一组线程)必须坐满,才启动车辆。

CyclicBarrier示例 

import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.util.concurrent.atomic.*;

/**
* 任务
*/
class PlayTask implements Runnable
{
    private CyclicBarrier cb;

    public PlayTask(CyclicBarrier cb){
	this.cb = cb;
    }

    public void run(){
	try{
	    long sleepTime = (long)(Math.random() * 2500) ;
            String tName = Thread.currentThread().getName();
	    System.out.println(tName+":在做事,需要耗时["+sleepTime+"]");
	    Thread.sleep(sleepTime);
	    cb.await();
	    System.out.println("凑够"+cb.getParties()+",["+tName+"]已上车,出发!");
	}
	catch(InterruptedException | BrokenBarrierException e){
	}
	finally{
	}
    }
}

/**
* 测试
*/
public class CyclicBarrierTest 
{
    public static void main(String[] args) 
    {
	int count = 3 ; //计数量

	CyclicBarrier cb = new CyclicBarrier(count,new Runnable() {

	    public void run(){
		System.out.println("\n\r有"+count+"个线程到达...");
	    }
	});

	System.out.println("景区,请排队上车观光,每"+count+"人一组上车...\n\r");

	PlayTask eTask = new PlayTask(cb);	//任务:凑够3人去观光		

        Thread[] threads = new Thread[count*3]; //多线程,每count个一组

	for (int i = 0 ; i < count*3 ; ++i) { 
	    threads[i] = new Thread(eTask,"T"+i);
	    threads[i].start();
	} 
    }
}

程序结果如下:

景区,请排队上车观光,每3人一组上车...

T5:在做事,需要耗时[1717]
T3:在做事,需要耗时[2117]
T8:在做事,需要耗时[1261]
T1:在做事,需要耗时[286]
T0:在做事,需要耗时[173]
T4:在做事,需要耗时[221]
T6:在做事,需要耗时[2319]
T7:在做事,需要耗时[1538]
T2:在做事,需要耗时[1259]

有3个线程到达...
凑够3,[T1]已上车,出发!
凑够3,[T4]已上车,出发!
凑够3,[T0]已上车,出发!

有3个线程到达...
凑够3,[T7]已上车,出发!
凑够3,[T8]已上车,出发!
凑够3,[T2]已上车,出发!

有3个线程到达...
凑够3,[T6]已上车,出发!
凑够3,[T3]已上车,出发!
凑够3,[T5]已上车,出发!

 CyclicBarrier和CountDownLatch区别

1。

CountDownLatch:主线程等在这里,直到N个子线程到达后,主线程才得以继续往下执行;

CyclicBarrier:则是N个子线程构成一组,组内成员有快有慢,快的成员到达屏障后,无法通过,必须组内的所有成员都忙好到达了屏障,屏障才会打开放行这一组内的N个子线程,使得这N个子线程得以通过屏障继续执行,与主线程没关系,是一组内的N个子线程等待彼此都到达屏障。如果非要说和CountDownLatch类似的地方就是,构造函数public CyclicBarrier(int parties, Runnable barrierAction)这样的,这里的Runnable就是N个子线程都到达了之后,才能得以执行。

2。

CountDownLatch计数为0后,无法重置,一次性的;

CyclicBarrier计数达到后,自动重置,可循环利用的。
 

 

           

猜你喜欢

转载自blog.csdn.net/mmlz00/article/details/83750332