talosのプログラミング教室

Java Gold合格への道 ~並行処理・CyclicBarrier~

スポンサーリンク

こんにちは。たろすです。

今回は並行処理におけるCyclicBarrierについて説明します。

CyclicBarrierとは

CyclicBarrierは並行処理において各スレッドの足並みをそろえるときに使うクラスです。

例えば以下のようなコードを実行すると、

public class Main {

	public static void main(String[] args) {

		Thread t0 = new Thread(new Runnable() {
			public void run() {
				String threadName = Thread.currentThread().getName();
				System.out.println(threadName + ":start");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(threadName + ":end");
			}
		});

		Thread t1 = new Thread(new Runnable() {
			public void run() {
				String threadName = Thread.currentThread().getName();
				System.out.println(threadName + ":start");
				try {
					Thread.sleep(5000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(threadName + ":end");
			}
		});

		Thread t2 = new Thread(new Runnable() {
			public void run() {
				String threadName = Thread.currentThread().getName();
				System.out.println(threadName + ":start");
				try {
					Thread.sleep(10000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(threadName + ":end");
			}
		});

		t0.start();
		t1.start();
		t2.start();
	}
}
Thread-0:start
Thread-1:start
Thread-2:start
Thread-0:end
Thread-1:end
Thread-2:end

「Thread-0:end」と表示された後約4秒後に「Thread-1:end」と表示され、さらに約5秒後に「Thread-2:end」と表示されます。

一方で、CyclicBarrierを使うと、

public class Main {

	public static void main(String[] args) {

		CyclicBarrier barrier = new CyclicBarrier(2);

		Thread t0 = new Thread(new Runnable() {
			public void run() {
				String threadName = Thread.currentThread().getName();
				System.out.println(threadName + ":start");
				try {
					Thread.sleep(1000);
					barrier.await();
				} catch (InterruptedException | BrokenBarrierException e) {
					e.printStackTrace();
				}
				System.out.println(threadName + ":end");
			}
		});

		Thread t1 = new Thread(new Runnable() {
			public void run() {
				String threadName = Thread.currentThread().getName();
				System.out.println(threadName + ":start");
				try {
					Thread.sleep(5000);
					barrier.await();
				} catch (InterruptedException | BrokenBarrierException e) {
					e.printStackTrace();
				}
				System.out.println(threadName + ":end");
			}
		});

		Thread t2 = new Thread(new Runnable() {
			public void run() {
				String threadName = Thread.currentThread().getName();
				System.out.println(threadName + ":start");
				try {
					Thread.sleep(10000);
					barrier.await();
				} catch (InterruptedException | BrokenBarrierException e) {
					e.printStackTrace();
				}
				System.out.println(threadName + ":end");
			}
		});

		t0.start();
		t1.start();
		t2.start();
	}
}
Thread-1:start
Thread-0:start
Thread-2:start
Thread-1:end
Thread-0:end

実行から約10秒経ってから「Thread-0:end」と「Thread-1:end」が同時に表示されます。

ちなみに「Thread-2:end」が表示されないのはコンストラクタの引数(足並みをそろえるスレッドの数)を2に設定しているからです。

先に到着した二つのスレッドは先に行ってしまい、残りのスレッドは一つになってしまったので最後のスレッドは永遠に待機状態のままです。

おわりに

今回は並行処理におけるCyclicBarrierについて説明しました。