Java併發之CyclicBarrier(在集合點同步)

chi633發表於2017-12-21
  • CyclicBarrier引入
  • 建立CyclicBarrier
  • 遇到CyclicBarrier之後休眠
  • CyclicBarrier的回撥執行緒
  • CyclicBarrier的簡單例子
  • CyclicBarrier進行分治程式設計的例子

CyclicBarrier引入

CyclicBarrier類是一個同步輔助類,類似於CountDownLatch,但遠比CountDownLatch要強大。CyclicBarrier 的字面意思是可迴圈使用(Cyclic)的屏障(Barrier)。它要做的事情是,讓一組執行緒到達一個屏障(也可以叫同步點)時被阻塞,直到最後一個執行緒到達屏障時,屏障才會開門,所有被屏障攔截的執行緒才會繼續幹活。就如下面這個圖所示

image.png

CyclicBarrier相當於一個屏障插線上程執行的過程中,取決於執行緒呼叫await方法的位置,直到指定執行緒數量的到達之後,這個屏障才可以取走。

建立CyclicBarrier

當你建立一個CyclicBarrier類的時候,需要指定需要等待的執行緒數

CyclicBarrier barrier = new CyclicBarrier(2);
複製程式碼

遇到CyclicBarrier之後休眠

當線上程指定位置設定屏障的時候,只需要呼叫CyclicBarrier的await方法.

barrier.await();
複製程式碼

await方法還可以指定等待的時間。當達到這個等待的時間,即使沒有足夠的執行緒到達,這個屏障也會被解除

barrier.await(10, TimeUnit.SECONDS);
複製程式碼

終止執行緒遇到屏障之後的等待條件有下面這些:

  • 足夠的執行緒到達屏障處,自動解除屏障
  • 執行緒等待螢幕指定的等待時間之後,超時,解除屏障
  • 執行緒被中斷,其他執行緒被中斷,屏障會解除
  • 外部執行緒呼叫了CyclicBarrier.reset()方法,屏障解除。

CyclicBarrier的回撥執行緒

CyclicBarrier初始化的時候,可以傳入一個runnable物件作為初始化引數,當所有執行緒都到達屏障點後,屏障會先把這個指定的runnable物件作為執行緒來執行,執行完之後,就會移除屏障喚醒所有執行緒,這個特性很有作用,可以達到分治操作,fork/join。想象一下,我們讓執行緒在屏障前計算好各自的結果,然後當所有執行緒都算完之後,我們在回撥執行緒中執行統計所有計算結果,這樣就相當於分治技術了,將一個大任務切分給其他執行緒分成小任務各自執行,執行完之後就將他們彙總。

Runnable      barrierAction = ... ;
CyclicBarrier barrier       = new CyclicBarrier(2, barrierAction);
複製程式碼

CyclicBarrier的簡單例子

Runnable barrier1Action = new Runnable() {
    public void run() {
        System.out.println("BarrierAction 1 executed ");
    }
};
Runnable barrier2Action = new Runnable() {
    public void run() {
        System.out.println("BarrierAction 2 executed ");
    }
};

CyclicBarrier barrier1 = new CyclicBarrier(2, barrier1Action);
CyclicBarrier barrier2 = new CyclicBarrier(2, barrier2Action);

CyclicBarrierRunnable barrierRunnable1 =
        new CyclicBarrierRunnable(barrier1, barrier2);

CyclicBarrierRunnable barrierRunnable2 =
        new CyclicBarrierRunnable(barrier1, barrier2);

new Thread(barrierRunnable1).start();
new Thread(barrierRunnable2).start();
複製程式碼
public class CyclicBarrierRunnable implements Runnable{

    CyclicBarrier barrier1 = null;
    CyclicBarrier barrier2 = null;

    public CyclicBarrierRunnable(
            CyclicBarrier barrier1,
            CyclicBarrier barrier2) {

        this.barrier1 = barrier1;
        this.barrier2 = barrier2;
    }

    public void run() {
        try {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() +
                                " waiting at barrier 1");
            this.barrier1.await();

            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() +
                                " waiting at barrier 2");
            this.barrier2.await();

            System.out.println(Thread.currentThread().getName() +
                                " done!");

        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}
複製程式碼

執行結果

image.png

CyclicBarrier進行分治程式設計的例子

我們實現一個CyclicBarrier分治程式設計的例子 我們假設現在一個陣列中一個元素出現的次數,我們分出幾個執行緒分別計算不同的行,讓他們算完之後在屏障那裡wait,然後等所有執行緒都算完了,我們就可以呼叫回撥執行緒來計算總的結果

大陣列類

package CyclicBarrier;

import java.util.Random;

public class MatrixMock {
	private int[][] data;
	
	public MatrixMock(int size, int length, int number) {
		int counter = 0;
		data = new int[size][length];
		Random random = new Random();
		
		for(int i=0;i<size;i++) {
			for(int j=0;j<length;j++) {
				data[i][j] = random.nextInt(10);
				if(data[i][j] == number)
					counter++;
			}
		}
		
		System.out.println("矩陣中有 " + counter + " 個要查詢的數 " + number);
	}
	
	public int[] getRow(int row) {
		if((row >= 0) && (row < data.length))
			return data[row];
		return null;
	}
}

複製程式碼

結果類:

package CyclicBarrier;

public class Results {
	private int[] data;
	
	public Results(int size) {
		data = new int[size];
	}
	
	public void setData(int position, int value) {
		data[position] = value;
	}
	
	public int[] getData() {
		return data;
	}
}

複製程式碼

搜尋執行緒

package CyclicBarrier;

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

public class Searcher implements Runnable {

	private int firstRow;
	private int lastRow;
	
	private MatrixMock mock;
	
	private Results results;
	private int number;
	
	private final CyclicBarrier barrier;
	
	
	
	public Searcher(int firstRow, int lastRow, MatrixMock mock, Results results, int number, CyclicBarrier barrier) {
		super();
		this.firstRow = firstRow;
		this.lastRow = lastRow;
		this.mock = mock;
		this.results = results;
		this.number = number;
		this.barrier = barrier;
	}



	@Override
	public void run() {
		int counter;
		
		System.out.println(Thread.currentThread().getName() + "正在搜尋資料" + firstRow + " " + lastRow);
		
		for(int i=firstRow;i<lastRow;i++) {
			int[] row = mock.getRow(i);
			counter = 0;
			for(int j=0;j<row.length;j++) {
				if(row[j] == number)
					counter++;
			}
			results.setData(i, counter);
		}
		
		System.out.println(Thread.currentThread().getName() + "查完了");
		
		try {
			barrier.await();
		} catch (InterruptedException | BrokenBarrierException e) {
			e.printStackTrace();
		}
		
		System.out.println(Thread.currentThread().getName() + "終於等到了");
	}

}

複製程式碼

回撥執行緒統計結果

package CyclicBarrier;

public class Grouper implements Runnable {

	private Results results;
	
	public Grouper(Results results) {
		this.results = results;
	}
	
	
	@Override
	public void run() {
		int finalResult = 0;
		
		System.out.println("正在統計結果。。。");
		
		int[] data = results.getData();
		
		for(int number : data) {
			finalResult += number;
		}
		
		System.out.println(finalResult);
	}

}

複製程式碼

main類測試

package CyclicBarrier;

import java.util.concurrent.CyclicBarrier;

public class Main {

	public static void main(String[] args) {
		
		final int ROWS = 10000;
		final int NUMBERS = 10000;
		final int SEACHER = 5;
		final int PARTICIPANTS = 5;
		final int LINES_PARTICIPANTS = 2000;
		
		MatrixMock mock = new MatrixMock(ROWS, NUMBERS, SEACHER);
		
		Results results = new Results(ROWS);
		
		Grouper grouper = new Grouper(results);
		
		CyclicBarrier barrier = new CyclicBarrier(PARTICIPANTS, grouper);
		
		Searcher[] searchers = new Searcher[PARTICIPANTS];
		
		for(int i=0;i<PARTICIPANTS;i++) {
			searchers[i] = new Searcher(i*LINES_PARTICIPANTS, i*LINES_PARTICIPANTS + LINES_PARTICIPANTS, mock, results, 5, barrier);
			new Thread(searchers[i]).start();
		}
	}

}

複製程式碼

執行結果

image.png

相關文章