とあるプロジェクトでCountDownLatch
が頻繁に出てきた。
こいつは一体何をしてくれる子なんだ?ってことで調べてみた。
以下にスレッド処理を行うサンプルコードを示しながら説明していく。
各スレッドが「wait...」を標準出力し、全スレッドの処理が完了したら「finish!」を出力したい、そんなサンプルコードである。
CountDownLatch未使用の場合
public class MyClass {
private static final int LOOP_COUNT= 10;
public static void main(String[] args) throws Exception {
for (int i = 0; i < LOOP_COUNT; i++) {
new Thread() {
public void run() {
System.out.println("wait...");
}
}.start();
}
System.out.println("finish!");
}
}
wait...
wait...
wait...
wait...
wait...
wait...
wait...
wait...
finish!
wait...
wait...
######※ 実行のタイミングで上に示した結果と異なる場合があります。
おやおや。。。
まだ終わってないのに「finish!」って出てしまったよw
というわけで本日の主役CountDownLatch
を使ってみましょう。
CountDownLatchを使用した場合
public class MyClass {
private static final int LOOP_COUNT= 10;
private static CountDownLatch countDownLatch = new CountDownLatch(LOOP_COUNT);
public static void main(String[] args) throws Exception {
for (int i = 0; i < LOOP_COUNT; i++) {
new Thread() {
public void run() {
System.out.println("wait...");
countDownLatch.countDown();
}
}.start();
}
countDownLatch.await();
System.out.println("finish!");
}
}
wait...
wait...
wait...
wait...
wait...
wait...
wait...
wait...
wait...
wait...
finish!
######※上記は何度実行しても同じ結果になります
「finish!」って正しいタイミングで言うようになったw
###サンプルコード【1】と【2】の違い
【2】のサンプルコードには以下の3行が加わっている。
1.総スレッド数分でCountDownLatch
を生成
private static CountDownLatch countDownLatch = new CountDownLatch(LOOP_COUNT);
2.countDownLatch
をカウントダウン
初期がLOOP_COUNT
(=10)なので、このメソッドが呼ばれる毎に9,8,7...とカウントダウンされていく。
countDownLatch.countDown();
3.全スレッドの処理が完了するのを待つ
具体的にはcountDownLatch
が0になるまで待機する。
countDownLatch.await();
###まとめ
スレッド処理でファイル書き込みをしていたり、何かしら処理は分散させて早くしたいけど、次の処理に進むのは全部終わってからじゃないとダメなの!って時にとても有効ですね。
やたら頻繁に目にした理由がわかりました。
次はExecutorService
,Semaphore
辺りを調べたいなと思います。
###参考サイト
- http://otn.oracle.co.jp/technology/global/jp/sdn/java/private/techtips/2005/tt0216.html
- http://d.hatena.ne.jp/nowokay/20081127/1227748012
###~~(本っ当に心底どうでもいい)~~おまけ
参考サイト(※1)のサンプルコードをちょっといじって顔文字が流れるようにしてみたら勉強が捗りましたw
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import javax.swing.JFrame;
import javax.swing.JTextField;
import lombok.AllArgsConstructor;
public class SemaphoreSample {
private static final int THREAD_COUNT = 10;
private static final int ACTIVE_COUNT = 3;
private static Semaphore semaphoe = new Semaphore(ACTIVE_COUNT);
private static CountDownLatch countDown = new CountDownLatch(THREAD_COUNT);
public static void main(String args[]) throws Exception {
JFrame f = new JFrame("Sample");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(0, 1));
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
f.setBounds((screenSize.width - 400) / 2,
(screenSize.height - 300) / 2, 400, 300);
ExecutorService ex = Executors.newFixedThreadPool(THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; ++i) {
JTextField tf = new JTextField();
f.add(tf);
Runner r = new Runner(tf);
ex.submit(r);
}
f.setVisible(true);
countDown.await();
System.out.println("finish!");
}
@AllArgsConstructor
private static class Runner implements Runnable {
private JTextField tf;
@Override
public void run() {
try {
String tr = "(^・ ω ・^)";
for (int j = 0; j < 50; ++j) {
tf.setText(tr);
tr = " " + tr;
Thread.sleep(50);
}
semaphoe.acquire();
for (int i = 0; i < 50; ++i) {
tf.setText(tr);
tr = " " + tr;
Thread.sleep(50);
}
} catch (Exception ex) {
} finally {
semaphoe.release();
countDown.countDown();
}
}
}
}
どうなったかは実行してみてねw