第五章 (4): シンクロナイザ

シンクロナイザは自身のステートを用いてスレッドのコントロールフローを調停する機能。色々な種類がある。

Latch

Latchはゲートみたいなもの。Latchが最終ステートに到達するまでは、全てのスレッドが先に進むのをブロックするが、最終ステートに達すると以降ゲートは開きっ放しになる。Latchは1回限りの活動のチェックに使う。例えばアプリケーション起動時の、リソースの初期化待ちなど。

FutureTask

Executorなどのタスク結果の状態を返す。

Semaphore

何かのリソースにアクセスする活動の同時実行数の制御を行う場合に使う。リソースプールの実装や、コレクションのサイズ制限などに使うのが一般的。分かりづらいので例を書いてみる。

public static void main(String[] args) {

	final Thread st1 = new Thread() {
		@Override
		public void run() {
			BoundedHashSet<String> set1 = new BoundedHashSet<String>(0);
			try {
				set1.add("ignore");
				System.out.println(set1);
			} catch (InterruptedException e) {
				System.out.println("interrupted add operation: " + set1);
			}
		}
	};

	st1.start(); // 追加待ちのままブロックされているため、出力されない
	System.out.println("st1 started.");

	new Thread() {
		@Override
		public void run() {
			st1.interrupt();
			// st1に割り込みを行うと, BoundedHashSetのaddメソッドはInterruptedExceptionを投げる
			System.out.println("interrupt st1.");
		}
	}.start();

}

public static class BoundedHashSet<T> {

	private final Set<T> delegate;
	private final Semaphore semahore;

	public BoundedHashSet(int bound) {
		this.delegate = Collections.synchronizedSet(new HashSet<T>());
		this.semahore = new Semaphore(bound);
	}

	public boolean add(T o) throws InterruptedException {
		// 追加許可を得る: 得られなければブロックする
		semahore.acquire();
		boolean wasAdded = false;
		try {
			wasAdded = delegate.add(o);
			return wasAdded;
		} finally {
			if (!wasAdded) {
				// 追加されなかった場合, 取得した許可をリリースする
				semahore.release();
			}
		}
	}

	public boolean remove(T o) {
		boolean wasRemoved = delegate.remove(o);
		if (wasRemoved) {
			// 得た許可を1つ減らす(追加可能な数を1つ増やす)
			semahore.release();
		}
		return wasRemoved;
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		synchronized (delegate) {
			for (T o : delegate) {
				builder.append(o);
			}
		}
		return builder.toString();
	}

}
st1 started.
interrupt st1.
interrupted add operation: 

セマフォが許可しているサイズは0個なので、このコレクションに追加することは出来ない。そのため、st1スレッドはスタートしても待機状態になる。ここに別スレッドで割り込みをかけている。