1. 程式人生 > >Java併發程式設計之——CyclicBarrier的使用

Java併發程式設計之——CyclicBarrier的使用

首先看一下官方關於CyclicBarrier的簡介:

/**
 * A synchronization aid that allows a set of threads to all wait for
 * each other to reach a common barrier point.  CyclicBarriers are
 * useful in programs involving a fixed sized party of threads that
 * must occasionally wait for each other. The barrier is called
 * <em>cyclic</em> because it can be re-used after the waiting threads
 * are released.

這段話的意思是:CyclicBarrier允許一組執行緒相互等待達到一個公共的障礙點。CyclicBarrier對於一組執行緒必須相互等待的場景很有用。比如有一組執行緒,都要往資料庫裡面寫入操作,只有當所有的執行緒都往資料庫裡面寫入資料之後,這些執行緒才能繼續往下執行,這時候就可以使用CyclicBarrier了。當所有的等待執行緒釋放之後,CyclicBarrier是可重用的。

CyclicBarrier有兩個建構函式:

 public CyclicBarrier(int parties, Runnable barrierAction)
 public CyclicBarrier(int parties) 
引數parties指讓多少個執行緒或者任務等待至barrier狀態;引數barrierAction為當這些執行緒都達到barrier狀態時會執行的內容。

對於CyclicBarrier來說,最重要的是await()方法:

public int await() throws InterruptedException, BrokenBarrierException
public int await(long timeout, TimeUnit unit)

第一個版本比較常用,用來掛起當前執行緒,直至所有執行緒都到達barrier狀態再同時執行後續任務;
第二個版本是讓這些執行緒等待至一定的時間,如果還有執行緒沒有到達barrier狀態就直接讓到達barrier的執行緒執行後續任務。

有一個需求:有幾個同學約好一起去食堂吃飯,各自都從各自的宿舍出發,然後到宿舍樓下集合。當所有的人都到了宿舍樓下之後,再一起從宿舍樓下出發前往食堂吃飯。

下面看程式碼實現:

package com.easyliu.java.demo.cyclicbarrier;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CyclicBarrierTest {
	private static final int THREAD_NUMBER = 3;
	private static CyclicBarrier sCyclicBarrier = new CyclicBarrier(
			THREAD_NUMBER, new Runnable() {

				@Override
				public void run() {
					System.out.println("大家都到達了宿舍樓下,一起出發吧。。。");
				}
			});

	public static void main(String[] args) {
		ExecutorService executorService = Executors
				.newFixedThreadPool(THREAD_NUMBER);
		for (int i = 0; i < THREAD_NUMBER; i++) {
			executorService.execute(new WalkFromDomitoryToCanteenRunnable(
					sCyclicBarrier, "同學" + i));
		}
		try {
			Thread.sleep(10000);//主執行緒睡眠
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("CyclicBarrier重用");
		for (int i = THREAD_NUMBER; i < THREAD_NUMBER * 2; i++) {
			executorService.execute(new WalkFromDomitoryToCanteenRunnable(
					sCyclicBarrier, "同學" + i));
		}
	}

	/**
	 * 從宿舍到食堂執行緒
	 * 
	 * @author LiuYi
	 *
	 */
	public static class WalkFromDomitoryToCanteenRunnable implements Runnable {
		private CyclicBarrier mCyclicBarrier;
		private String mName;

		public WalkFromDomitoryToCanteenRunnable(CyclicBarrier cyclicBarrier,
				String name) {
			this.mCyclicBarrier = cyclicBarrier;
			this.mName = name;
		}

		@Override
		public void run() {
			System.out.println(mName + "開始從宿舍出發。。。");
			try {
				Thread.sleep(1000);
				mCyclicBarrier.await();// 等待別同學
				// 前往食堂
				System.out.println(mName + "開始從宿舍樓下出發。。。");
				Thread.sleep(1000);
				System.out.println(mName + "達到食堂。。。");
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			}
		}
	}
}



輸出結果如下:
同學1開始從宿舍出發。。。
同學0開始從宿舍出發。。。
同學2開始從宿舍出發。。。
大家都到達了宿舍樓下,一起出發吧。。。
同學1開始從宿舍樓下出發。。。
同學2開始從宿舍樓下出發。。。
同學0開始從宿舍樓下出發。。。
同學0達到食堂。。。
同學1達到食堂。。。
同學2達到食堂。。。
CyclicBarrier重用
同學3開始從宿舍出發。。。
同學5開始從宿舍出發。。。
同學4開始從宿舍出發。。。
大家都到達了宿舍樓下,一起出發吧。。。
同學5開始從宿舍樓下出發。。。
同學4開始從宿舍樓下出發。。。
同學3開始從宿舍樓下出發。。。
同學4達到食堂。。。
同學3達到食堂。。。
同學5達到食堂。。。



從輸出結果可以看出實現了我們想要的效果,並且實現了CyclicBarrier的重用,因為初始化CyclicBarrier的時候只設置了讓三個執行緒等待至barrier狀態,也就是當有三個同學到達了宿舍樓下之後,就一起走。剩下的三個同學一起走。

CountDownLatch和CyclicBarrier都能夠實現執行緒之間的等待,只不過它們側重點不同:
CountDownLatch一般用於某個執行緒A等待若干個其他執行緒執行完任務之後,它才執行;
而CyclicBarrier一般用於一組執行緒互相等待至某個狀態,然後這一組執行緒再同時執行;
另外,CountDownLatch是不能夠重用的,而CyclicBarrier是可以重用的。