GNU/Linux >> Linux の 問題 >  >> Linux

Java でスプリアス ウェイク アップを強制する

あなたが参照した元の質問 (ウィキペディアの記事の限り) は、プロセスがシグナルされる副作用として、pthread の Linux 実装で偽のウェイクアップが発生すると述べています。 .あなたの質問から、Object.notify() (Java 内部スレッド間通信メソッド) で "signal" (Linux プロセス間通信メソッド) を見逃したようです。

誤ったウェイクアップを観察したい場合は、Java プログラムを実行して何らかのシグナルを送信する必要があります。


簡単なJavaプロセスシグナル(QUIT、STOP、CONTなど)を送信して、Linuxで簡単なテストを試みました。これらは偽のウェイクアップを引き起こさなかったようです.

そのため (少なくとも私には)、Linux シグナルが Java で偽のウェイクアップを引き起こす条件はまだ明確ではありません.


「Spurious wakeup」は寄せ集めで、あらゆるをカバーしています その領域での実装の詳細。したがって、「本物の」スプリアス ウェイクアップとは何か、また別のウェイクアップが「非現実的」である理由を理解することは非常に困難です。 「カーネル」、「システム ライブラリ (libc)」、「JVM」、「Java 標準ライブラリ (rt.jar)」、またはこのスタック上に構築されたカスタム フレームワークからいずれかを選択してください。

次のプログラムは、java.util.concurrent を使用した誤ったウェイクアップを示しています。 スタッフ:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SpuriousWakeupRWLock {
    static Lock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();
    static int itemsReady;

    public static void main(String[] args) throws Exception {

        // let consumer 1 enter condition wait
        new ConsumerOne().start();
        Thread.sleep(500);

        lock.lock();
        try {
            // let consumer 2 hit the lock
            new ConsumerTwo().start();
            Thread.sleep(500);

            // make condition true and signal one (!) consumer
            System.out.println("Producer: fill queue");
            itemsReady = 1;
            condition.signal();
            Thread.sleep(500);
        }
        finally {
            // release lock
            lock.unlock();
        } 

        System.out.println("Producer: released lock");
        Thread.sleep(500);
    }

    abstract static class AbstractConsumer extends Thread {
        @Override
        public void run() {
            lock.lock();
            try {
                consume();
            } catch(Exception e){
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        abstract void consume() throws Exception;
    }

    static class ConsumerOne extends AbstractConsumer {
        @Override
        public void consume() throws InterruptedException {
            if( itemsReady <= 0 ){      // usually this is "while"
                System.out.println("One: Waiting...");
                condition.await();
                if( itemsReady <= 0 )
                    System.out.println("One: Spurious Wakeup! Condition NOT true!");
                else {
                    System.out.println("One: Wakeup! Let's work!");
                    --itemsReady;
                }
            }
        }
    }

    static class ConsumerTwo extends AbstractConsumer {
        @Override
        public void consume() {
            if( itemsReady <= 0 )
                System.out.println("Two: Got lock, but no work!");
            else {
                System.out.println("Two: Got lock and immediatly start working!");
                --itemsReady;
            }
        }
    }
}

出力:

One: Waiting...
Producer: fill queue
Producer: released lock
Two: Got lock and immediatly start working!
One: Spurious Wakeup! Condition NOT true!

使用された JDK は次のとおりです:

java version "1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.9) (6b20-1.9.9-0ubuntu1~10.04.2)
OpenJDK 64-Bit Server VM (build 19.0-b09, mixed mode)

java.util.concurrent の 1 つの実装の詳細に基づいています。 :標準の Lock Condition という 1 つの待ち行列があります。 別の待ち行列があります。条件が通知された場合、通知されたスレッドは条件のキューからロックのキューに移動されます。実装の詳細:キューの最後に移動されます .別のスレッドがすでにロック キューで待機しており、この 2 番目のスレッドが条件変数にアクセスしなかった場合、このスレッドはシグナルを「盗む」ことができます。実装が最初のスレッドを に配置した場合 2 番目のスレッドでは、これは起こりませんでした。この「ボーナス」は、最初のスレッドがすでに一度ロックを取得しており、同じロックに関連付けられた状態での待機時間に基づいている可能性があります。 はそのスレッドにクレジットされています。

私はこれを「偽物」と定義します。

  • 状態が 1 回だけ通知されている
  • この状態で目覚めたスレッドは 1 つだけです
  • しかし、条件によって目覚めたスレッドは、それが真実ではないことを発見しました
  • もう一方のスレッドはこの状態に一度も触れていないため、「幸運ですが無実」です
  • もう少し実装を変更すれば、これを防ぐことができたはずです。

最後のポイントは、 Object.wait() を使用したこのコードで示されています :

public class SpuriousWakeupObject {
    static Object lock = new Object();
    static int itemsReady;

    public static void main(String[] args) throws Exception {

        // let consumer 1 enter condition wait
        new ConsumerOne().start();
        Thread.sleep(500);

        // let consumer 2 hit the lock
        synchronized (lock) {
            new ConsumerTwo().start();
            Thread.sleep(500);

            // make condition true and signal one (!) consumer
            System.out.println("Producer: fill queue");
            itemsReady = 1;
            lock.notify();

            Thread.sleep(500);
        } // release lock
        System.out.println("Producer: released lock");
        Thread.sleep(500);
    }

    abstract static class AbstractConsumer extends Thread {
        @Override
        public void run() {
            try {
                synchronized(lock){
                    consume();
                }
            } catch(Exception e){
                e.printStackTrace();
            }
        }
        abstract void consume() throws Exception;
    }

    static class ConsumerOne extends AbstractConsumer {
        @Override
        public void consume() throws InterruptedException {
            if( itemsReady <= 0 ){      // usually this is "while"
                System.out.println("One: Waiting...");
                lock.wait();
                if( itemsReady <= 0 )
                    System.out.println("One: Spurious Wakeup! Condition NOT true!");
                else {
                    System.out.println("One: Wakeup! Let's work!");
                    --itemsReady;
                }
            }
        }
    }

    static class ConsumerTwo extends AbstractConsumer {
        @Override
        public void consume() {
            if( itemsReady <= 0 )
                System.out.println("Two: Got lock, but no work!");
            else {
                System.out.println("Two: Got lock and immediatly start working!");
                --itemsReady;
            }
        }
    }
}

出力:

One: Waiting...
Producer: fill queue
Producer: released lock
One: Wakeup! Let's work!
Two: Got lock, but no work!

ここで、実装は期待どおりに動作しているように見えます:条件を使用するスレッドが最初に呼び出されます。

最後の注意: アイデア 原則は、なぜjava.util.concurrent.ArrayBlockingQueueがawait()の呼び出しの周りに「if」ではなく「while」ループを使用するのですか? 、私の解釈は異なりますが、コードは私からのものです.


偽のウェイクアップを強制することはできませんが、実行中のスレッドにとって、偽のウェイクアップは通常のウェイクアップと区別できません (イベントのソースは異なりますが、イベント自体は同じです)

偽のウェイクアップをシミュレートするには、notify(); を呼び出すだけです。

interrupt() を呼び出す 適切ではありません。これを行うと、割り込みフラグが設定され、偽のウェイクアップの後、割り込みフラグが not になるためです。 セット


Linux
  1. LinuxにJavaを手動でインストールする

  2. Caps Lockの遅延?

  3. Java 10 Webupd8?

  1. 修正E:ロックを取得できませんでした/ var / cache / apt / archives /lock[クイックヒント]

  2. Aptロックを保持しているプロセスを強制終了する方法は?

  3. Ubuntu 22.04 に Java をインストールする方法

  1. スピンロックとセマフォ

  2. Java 音声認識

  3. JavaでサブプロセスIDを取得