第五章 (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スレッドはスタートしても待機状態になる。ここに別スレッドで割り込みをかけている。