Java マルチスレッド シリーズ III (wait+notify+notifyAll)

1. wait、notify、notifyAll の最初の紹介

スレッドのプリエンプティブ実行により、スレッド間のスケジューリングがランダムで無秩序になることがわかっています。ただし、シナリオによっては、複数のスレッドの実行順序を合理的に調整する必要があります。join を使用するとスレッドの実行順序を制御できることはわかっていますが、join1 つのスレッドが実行を終了してから別のスレッドを実行できるのは限られており、機能も制限されています。したがって、スレッドの実行順序をより柔軟に制御するために、waitや などの一連の API を導入しました。notify/notifyAll

例えば:

上記の状況では、次の方法を使用してwait/notify効果的に解決できます。

Zhang San は、ATM にお金がないことがわかると、ロックを解除して ATM マシンから出て、誰かが ATM マシンにお金を入金するまでブロックして待ちます (一時的に CPU スケジューリングとロック競合に参加しません)。時間が来たら、Zhang San を起こして (通知し)、ATM ロック コンテストに参加し続けることができます。

2. wait、notify、notifyAll 関数の概要

1、待つ()

wait(): 条件が満たされていない/時間が成熟していないことがわかった場合は、wait() を呼び出してブロックして待機し、同時に現在のロックを解放します。特定の条件が満たされると、目覚めて取得を試みることができます再びロック。

: wait() を使用してブロックして待機すると、スレッドが WAITING 状態になります。

待機終了条件:

  1. 他のスレッドはオブジェクトの通知メソッドを呼び出します。
  2. wait wait タイムアウト (wait メソッドには、待機時間を指定するタイムアウト パラメーターを備えたバージョンが用意されています)。
  3. 他のスレッドが待機中のスレッドの割り込みメソッドを呼び出すと、wait が InterruptedException 例外をスローします。

2、通知()

notify(): 他のスレッドが成熟した条件を構築すると、notify() を呼び出してそれをウェイクアップできます。

通知に関する注意事項:

  1. Notify() メソッドは、同期メソッドまたは同期ブロックでも呼び出す必要があります。このメソッドは、オブジェクトのオブジェクト ロックを待機している可能性がある他のスレッドに通知し、それらのスレッドがオブジェクト ロックを再取得できるようにするために使用されます。物体。
  2. 複数のスレッドが待機している場合、スレッド スケジューラは待機状態のスレッドをランダムに選択します。(「先着順」はございません)
  3. Notice() メソッドの後、現在のスレッドはオブジェクト ロックをすぐには解放しません。オブジェクト ロックは、notify() メソッドを実行しているスレッドがプログラムの実行を終了するまで、つまり同期されたコード ブロックを終了するまで解放されません。

3、すべてに通知()

notifyAll():notify() は待機中のスレッドを起動するだけです。同じオブジェクトを待っているすべてのスレッドを一度に起動するには、notifyAll() を使用します。

すべてに通知する 注:

複数のスレッドが同時に起動されますが、これらの複数のスレッドはロックを競合する必要があるため、同時に実行されることはありませんが、最初にロックを取得したスレッドが最初に実行されます。

4. wait、notify、notifyAll の要点まとめ

  1. wait/notify/notifyAllはいずれもObjectのメソッドなので、クラスオブジェクトであればwait、notify、notifyAllを使用できます。
  2. wait/notify/notifyAll は synchronized とともに使用する必要があります。synchronized なしで使用すると、例外が直接スローされます。IllegalMonitorStateException
  3. 同期ロック オブジェクトは、wait/notify/notifyAll メソッドを呼び出すオブジェクトと同じである必要があります。
  4. ロックされたオブジェクトの場合は、waitを実行してからnotify/notifyAllを実行すると効果が現れますが、waitがない場合はnotifyは「一発で全部殺す」のと同じことになります。このときwaitは復帰できませんので、ただし、コード内で他の例外は発生しません。

5. 待機・通知の使用例

public class ThreadExample_wait_notify {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Object object = new Object();
        Thread t1 = new Thread(()->{
    
    
           synchronized (object) {
    
    
               try {
    
    
                   System.out.println("wait开始");
                   object.wait();
                   System.out.println("wait结束");
               } catch (InterruptedException e) {
    
    
                   e.printStackTrace();
               }
           }
        });

        Thread t2 = new Thread(()->{
    
    
           synchronized (object) {
    
    
               System.out.println("notify开始");
               object.notify();
               System.out.println("notify结束");
           }
        });
        // 等待t1中wait解锁阻塞,否则t2中的notify没有实质效果
        t1.start();
        Thread.sleep(1000);
        t2.start();
    }
}

結果の説明:

上記のコードは、t1 が最初にロックを取得します。wait が実行されると、t1 はブロックされ、ロックが解除されます。1 秒後に、t2 がロックを取得します。notify が実行されると、t1 スレッドは起動されますが、notify 直後にはロックは解放されませんt2 コード ブロックの場合、ロックはロジックの実行後にのみ解放され、t1 スレッドはロックを取得して実行を継続できます。

3.待機、参加、睡眠のまとめ

(1) 待機
wait()方法はオブジェクトクラス提供されたインスタンス メソッドは、他のスレッドがオブジェクトの Notice() メソッドまたは NotifyAll() メソッドを呼び出すまで、スレッドを待機状態にすることができます。これは通常、プロデューサー/コンシューマー モデル (後述) などのスレッド間通信に使用されます。このモデルでは、コンシューマーは、新しいデータが利用可能であることをプロデューサーが通知するのを待つ必要があります。スレッドが wait() メソッドを呼び出すと、占有されていたロックが解放され、notify() メソッドまたは NoticeAll() メソッドが呼び出されるまで、スレッドのステータスは待機状態になります。

(2) 結合
join()方法は次のとおりです。Thread クラスによって提供されるメソッド 静的メソッド、呼び出されたスレッドの実行が完了するまで待機するために使用されます。メインスレッドでサブスレッドの join() メソッドを呼び出した後、メインスレッドは待機状態になり、サブスレッドの実行が完了するまで実行を継続しません。複数のスレッドが指定された順序で実行されるようにするために使用できます。

(3)睡眠
sleep()方法もThreadクラスが提供するインスタンスメソッドこれにより、現在のスレッドが一定期間実行を一時停止する可能性があります。スレッドが sleep() メソッドを呼び出しても、ロックは解放されず、スレッドのステータスは TIMED_WAITING になります。通常、プログラムの実行速度や時間を制御するために使用され、ループ内で特定の条件が発生するのを待機するために使用されることがよくあります。

一般的に、wait() メソッドはスレッド間の通信に使用されます。join() メソッドは、他のスレッドが実行を完了するのを待つために使用されます。sleep() メソッドは、現在のスレッドの実行を一時停止するために使用されます。使用上、wait は synchronized で使用する必要がありますが、sleep と join は必要ありません。つまり、本質的に比較可能性はありませんが、唯一の類似点は、スレッドの実行を一定期間放棄できることです。それらの使用シーン実際のニーズに応じて適切な方法を選択する必要があります。

おすすめ

転載: blog.csdn.net/LEE180501/article/details/130457892