JavaのThread.join()メソッド

1.概要

このチュートリアルでは、 Thread クラスのさまざまな join() メソッドについて説明します。これらのメソッドの詳細といくつかのサンプルコードについて説明します。

wait() および notify()メソッド と同様に、 join() もスレッド間同期のもう1つのメカニズムです。

wait() notify() の詳細については、 このチュートリアル を参照してください。

2. Thread.join() メソッド

joinメソッドは Thread クラスで定義されています。

public final void join()はInterruptedExceptionをスローする このスレッドが終了するのを待つ

  • スレッドで join() メソッドを呼び出すと、呼び出し側のスレッドは待機状態になります。参照されたスレッドが終了するまで待機状態になります。

次のコードでこの動作を確認できます。

class SampleThread extends Thread {
    public int processingCount = 0;

    SampleThread(int processingCount) {
        this.processingCount = processingCount;
        LOGGER.info("Thread Created");
    }

    @Override
    public void run() {
        LOGGER.info("Thread " + this.getName() + " started");
        while (processingCount > 0) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                LOGGER.info("Thread " + this.getName() + " interrupted");
            }
            processingCount--;
        }
        LOGGER.info("Thread " + this.getName() + " exiting");
    }
}

@Test
public void givenStartedThread__whenJoinCalled__waitsTillCompletion()
  throws InterruptedException {
    Thread t2 = new SampleThread(1);
    t2.start();
    LOGGER.info("Invoking join");
    t2.join();
    LOGGER.info("Returned from join");
    assertFalse(t2.isAlive());
}

コードを実行すると、次のような結果が予想されます。

INFO: Thread Created
INFO: Invoking join
INFO: Thread Thread-1 started
INFO: Thread Thread-1 exiting
INFO: Returned from join
  • join() メソッドは、参照されているスレッドが中断された場合にも返す可能性があります。この場合、メソッドは InterruptedException をスローします。

最後に、 参照されたスレッドがすでに終了しているか、まだ開始されていない場合、 join() メソッドの呼び出しはすぐに を返します。

Thread t1 = new SampleThread(0);
t1.join(); //returns immediately

3. Thread.join() タイムアウト付きメソッド

参照されているスレッドがブロックされているか、処理に時間がかかりすぎる場合、 join() メソッドは待機し続けます。呼び出し側スレッドが応答しなくなるため、これは問題になる可能性があります。このような状況に対処するために、オーバーロードされたバージョンの join() メソッドを使用してタイムアウト期間を指定できます。

  • join() メソッドをオーバーロードする2つのhttps://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#join-long-[期間限定バージョン]があります。**

public public void join(long millis )はInterruptedExceptionをスローします。 このスレッドが終了するまでの待ち時間は最大でもミリ秒です。タイムアウト0は永遠に待つことを意味します。」 __

__「public final void join(long millis、int nanos )は、InterruptedException このスレッドが終了するまでに最大 millis ミリ秒に加えて nanos ナノ秒の間のスローをスローします。」

以下のように時限の join() を使うことができます。

@Test
public void givenStartedThread__whenTimedJoinCalled__waitsUntilTimedout()
  throws InterruptedException {
    Thread t3 = new SampleThread(10);
    t3.start();
    t3.join(1000);
    assertTrue(t3.isAlive());
}

この場合、呼び出し側スレッドはスレッドt3が終了するのを約1秒待ちます。この期間内にスレッドt3が終了しない場合、 join() メソッドは呼び出し側のメソッドに制御を戻します。

  • Timed join() はタイミングに関してOSに依存します。そのため、 join() が指定された時間だけ待機するとは限りません。

4. Thread.join() メソッドと同期

終了まで待つことに加えて、 join() メソッドを呼び出すと同期効果があります。 ** join()はhttps://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.5[happens-before]関係を作成します。

「スレッド内のすべてのアクションは、他のスレッドがそのスレッド上のjoin()から正常に戻る前に発生する」

つまり、スレッドt1がt2.join()を呼び出すと、t2によって行われたすべての変更は、戻ったときにt1に表示されます。ただし、 join() を呼び出さなかったり、他の同期メカニズムを使用したりしない場合、他のスレッドが完了しても、他のスレッドの変更が現在のスレッドに表示されるという保証はありません。

したがって、終了状態にあるスレッドへの__join()メソッド呼び出しがすぐに戻ったとしても、状況によってはそれを呼び出す必要があります。

以下は、不適切に同期化されたコードの例です。

SampleThread t4 = new SampleThread(10);
t4.start();//not guaranteed to stop even if t4 finishes.
do {

} while (t4.processingCount > 0);

上記のコードを正しく同期させるには、ループ内にtimed __t4.join()を追加するか、他の同期メカニズムを使用します。

5.まとめ

join() メソッドはスレッド間同期に非常に役立ちます。この記事では、 join() メソッドとその動作について説明しました。また、 join() メソッドを使用してコードを確認しました。

いつものように、完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/core-java-concurrency[over on GitHub]にあります。