LoginSignup
73
68

More than 5 years have passed since last update.

CountDownLatchを使ってスレッド処理の完了を待つ

Posted at

とあるプロジェクトでCountDownLatchが頻繁に出てきた。
こいつは一体何をしてくれる子なんだ?ってことで調べてみた。

以下にスレッド処理を行うサンプルコードを示しながら説明していく。
各スレッドが「wait...」を標準出力し、全スレッドの処理が完了したら「finish!」を出力したい、そんなサンプルコードである。

CountDownLatch未使用の場合


【1】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!");
    }
}
【1】の実行結果
wait...
wait...
wait...
wait...
wait...
wait...
wait...
wait...
finish!
wait...
wait...
※ 実行のタイミングで上に示した結果と異なる場合があります。

おやおや。。。
まだ終わってないのに「finish!」って出てしまったよw

というわけで本日の主役CountDownLatchを使ってみましょう。

CountDownLatchを使用した場合


【2】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!");
    }
}
【2】の実行結果
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辺りを調べたいなと思います。

参考サイト


(本っ当に心底どうでもいい)おまけ


参考サイト(※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

73
68
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
73
68