1. 概要

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

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

このチュートリアルをざっと見て、 wait() notify()の詳細を読むことができます。

2. Thread.join()メソッド

結合メソッドは、スレッドクラスで定義されています。

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つのタイミングバージョンがあります。

「publicfinalvoidjoin(long ミリ秒 )InterruptedExceptionをスローしますせいぜい待つミリ秒このスレッドが終了するまでのミリ秒。 タイムアウト0は、永久に待機することを意味します。」

“ public final void join(long millis、int nanos )throws 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()は、発生前の関係を作成します:

「スレッド内のすべてのアクションが発生します-他のスレッドがそのスレッドの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);

上記のコードを適切に同期するには、ループ内に時間指定 t4.join()を追加するか、他の同期メカニズムを使用します。

5. 結論

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

いつものように、完全なソースコードはGitHubにあります。