技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

javaでのスレッド制御(joinとsynchronized)

javaでは、スレッドを立てて処理を並列に行うことができます。
しかし、並列に処理を行う際、処理順を制御しなければならないことがあります。
 
処理順の制御方法として基本的な方法として、joinを使う方法とsynchronizedを使う方法があります。
joinを使うことで、スレッドが終わるのを待つことができます。
また、synchronizedを使うことで、複数のスレッドが同じ処理を同時に行わないように制御することができます。
 
以下は、joinやsynchronizedを使って制御を行うサンプルコードです。
1つのスレッドで5回の処理を行うサンプルであり、そのスレッドを2つ立てます。
また、スレッド間で同一のオブジェクト(staticなオブジェクト)を共有しており、そのオブジェクトでは合計何回処理が行われたのかをカウントしています。
 
制御を行わない場合は、どちらのスレッドが先に処理するのかが定まりません。また、共有オブジェクトでのカウントを2つのスレッドで同時に行うためにカウントも上手く行きません。
joinによる制御を行う場合は、スレッド1の5回の処理が終わってからスレッド2の処理に移る、という制御が可能になります。
synchronizedによる制御を行う場合は、スレッド1とスレッド2が同時に動いても、共有オブジェクトでのカウントは同時には行われず、上手くカウントすることができます。
 
【サンプルコード】
・ThreadMain.java
public class ThreadMain {
    public static void main(String[] args) {

        // スレッドのインスタンスを作成
        ThreadBody test1 = new ThreadBody("スレッド1");
        ThreadBody test2 = new ThreadBody("スレッド2");

        // スレッド1の実行
        test1.start();

        // ①スレッド1が終了するまで待機
        /*
        try {
            test1.join();
        } catch (InterruptedException e) {
            // 例外処理
            e.printStackTrace();
        }
        */

        // スレッド2の実行
        test2.start();
    }
}
 
・ThreadBody.java
public class ThreadBody extends Thread{

    private String threadName = null;
    private static ThreadCounter threadCounter = new ThreadCounter();

    ThreadBody(String threadName){
        this.threadName = threadName;
    }

    public void run(){
        for (int i = 0; i < 5; i++) {
                threadCounter.countUp();
                System.out.println(threadName+"の"+(i+1)+"回目の処理");
        }
    }
}
 
・ThreadCounter.java
public class ThreadCounter {

    private int count;

    public void countUp() {
        // ②複数スレッドで同時実行されないよう同期を取る
        // synchronized (this) {
            // 1000ミリ秒スリープ
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count++;
            System.out.println("処理回数:"+count);
        // }
    }
}
 
【実行結果】
・そのまま実行した場合
処理回数:2
処理回数:2
スレッド1の1回目の処理
スレッド2の1回目の処理
処理回数:3
処理回数:4
スレッド1の2回目の処理
スレッド2の2回目の処理
処理回数:6
処理回数:6
スレッド2の3回目の処理
スレッド1の3回目の処理
処理回数:8
処理回数:8
スレッド2の4回目の処理
スレッド1の4回目の処理
処理回数:10
処理回数:10
スレッド2の5回目の処理
スレッド1の5回目の処理
 
・①のコメントアウトを外した場合(join)
処理回数:1
スレッド1の1回目の処理
処理回数:2
スレッド1の2回目の処理
処理回数:3
スレッド1の3回目の処理
処理回数:4
スレッド1の4回目の処理
処理回数:5
スレッド1の5回目の処理
処理回数:6
スレッド2の1回目の処理
処理回数:7
スレッド2の2回目の処理
処理回数:8
スレッド2の3回目の処理
処理回数:9
スレッド2の4回目の処理
処理回数:10
スレッド2の5回目の処理
 
・②のコメントアウトを外した場合(synchronized)
処理回数:1
スレッド1の1回目の処理
処理回数:2
スレッド2の1回目の処理
処理回数:3
スレッド1の2回目の処理
処理回数:4
スレッド2の2回目の処理
処理回数:5
スレッド1の3回目の処理
処理回数:6
スレッド2の3回目の処理
処理回数:7
スレッド1の4回目の処理
処理回数:8
スレッド2の4回目の処理
処理回数:9
スレッド1の5回目の処理
処理回数:10
スレッド2の5回目の処理