かとじゅんの技術日誌

技術の話をするところ

wait, notifyはもう古い

スレッドでシグナルを送受信する場合は、典型的にwait, notifyのAPIがよく使われてきました。
でも、Java5以降ではもう古いんですよ。そのやり方。concurrentパッケージにCountDownLatchというクラスがあります。これで同様のことがすごくシンプルに実現できるんです。(Seasar-usersでもwait/notify使った回答を投稿したのですが、Java5以降なら本当はこっちを使うべきです)

java.util.concurrent
クラス CountDownLatch

たとえば、以下のような複数スレッドの開始シグナルと終了シグナルで同期する場合です。
CountDownLatch自身が保持しているカウント数 - countDownメソッドが呼ばれた回数でゼロなったとき、awaitメソッドが待機せず、それ以外は待機します。その特性を使って開始や終了のシグナルを実装するわけです。ほら、簡単でしょ?

public class CountDownLatchTest {
	@Test
	public void test() {
		int threadCount = 3;
		final CountDownLatch startLatch = new CountDownLatch(1);
		final CountDownLatch endLatch = new CountDownLatch(threadCount);
		ExecutorService ex = Executors.newFixedThreadPool(3);
		for (int i = 1; i <= threadCount; i++) {
			final Integer index = Integer.valueOf(i);
			ex.submit(new Runnable() {
				public void run() {
					System.out.println("startLatch.await() start");
					try {
						startLatch.await();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println("startLatch.await() end");
					for (int j = 1; j < index.intValue() * 10; j++) {
						System.out.println("i = " + index + ", j = " + j);
					}
					System.out.println("endLatch.countDown() start");
					endLatch.countDown();
					System.out.println("endLatch.countDown() end");
				}
			});
			System.out.println("submit, i = " + i);
		}
		System.out.println("startLatch.countDown() start");
		startLatch.countDown();
		System.out.println("startLatch.countDown() end");
		System.out.println("endLatch.await() start");
		try {
			endLatch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("endLatch.await() end");
	}
}

追記:
Effective Javaを読み返したら、同じような事例が書かれていました。「項目69 waitとnofifyよりコンカレンシーユーティリティを選ぶ」をあわせてご覧ください。