Javaで偽のウェイクアップが実際に起こりますか?


208

さまざまなロック関連の質問を見て、(ほとんど)常に「偽のウェイクアップのためにループ」という用語を見つけています1おそらく、誰かがそのようなウェイクアップを経験しましたか(たとえば、適切なハードウェア/ソフトウェア環境を想定しています)?

「スプリアス」という言葉には明らかな理由がないことを知っていますが、そのようなイベントの理由は何ですか?

1注:ループの実行については問いません。)

編集:ヘルパーの質問(コードサンプルが好きな人向け):

次のプログラムを実行している場合:

public class Spurious {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition cond = lock.newCondition();
        lock.lock();
        try {
            try {
                cond.await();
                System.out.println("Spurious wakeup!");
            } catch (InterruptedException ex) {
                System.out.println("Just a regular interrupt.");
            }
        } finally {
            lock.unlock();
        }
    }
}

awaitランダムなイベントを永遠に待つことなく、これを偽って目覚めるにはどうすればよいですか


1
POSIXシステムで実行されpthread_cond_wait()、実際の質問を使用するJVMの場合、「なぜpthread_cond_waitに誤ったウェイクアップがあるのですか?」
フロー

回答:


204

偽の目覚めに関するウィキペディアの記事には、この一言があります。

pthread_cond_wait()Linux の関数は、futexシステムコールを使用して実装されます。Linuxの各ブロックシステムコールはEINTR、プロセスがシグナルを受信すると突然返されます。... システムコールのpthread_cond_wait()外にいた少しの時間で実際のウェイクアップを見逃す可能性があるため、待機を再開できませんfutex。この競合状態は、不変条件をチェックする呼び出し元によってのみ回避できます。したがって、POSIX信号は偽のウェイクアップを生成します。

概要:Linuxプロセスにシグナルが送られると、その待機中のスレッドはそれぞれ、素晴らしいホットなスプリアスウェークアップを享受します。

それを買います。これは、一般的にあいまいな「パフォーマンスのため」の理由がよく出されるよりも飲みやすい錠剤です。


13
ここでは、より良い説明:stackoverflow.com/questions/1461913/...
ギリ

3
このEINTRブロック解除は、Unix派生システムのすべてのブロックシステムコールに当てはまります。これによりカーネルが大幅に簡素化されましたが、アプリケーションプログラマーが負担を買いました。
ティムウィリスクロフト、2011

2
私はpthread_cond_wait()や友人がEINTRを返すことができないと思いましたが、誤って目覚めた場合はゼロを返しますか?From: pubs.opengroup.org/onlinepubs/7908799/xsh/… 「これらの関数は[EINTR]のエラーコードを返しません。」
gubby 2014

2
@jgubbyそうです。基になるfutex()呼び出しはを返しますEINTRが、その戻り値は次のレベルにバブルアップされません。したがって、pthreadの呼び出し元は、不変条件をチェックする必要があります。彼らが言っpthread_cond_wait()ていることは、待機が誤って起こされた可能性があるため、リターン時にループ条件(不変)を再度確認する必要があるということです。システムコール中に信号を受信することは1つの考えられる原因ですが、それだけが原因ではありません。
John Kugelman、2014

1
おそらく、pthreadライブラリは、その責任をユーザーに渡すのではなく、独自の不変条件と独自のチェックロジックを提供して、疑似ウェイクアップを排除することができます。それは(おそらく)主張されたパフォーマンスに影響を与えるでしょう。

22

私はこの行動を示す生産システムを持っています。スレッドは、キューにメッセージがあることを示すシグナルを待機します。忙しい期間では、ウェイクアップの最大20%が偽です(つまり、ウェイクアップすると、キューには何もありません)。このスレッドはメッセージの唯一のコンシューマです。Linux SLES-10 8プロセッサボックスで動作し、GCC 4.1.2で構築されています。メッセージが外部ソースから送信され、システムで十分に速く読み取られない場合に問題が発生するため、非同期的に処理されます。


15

タイトルで質問に答える- はい!それはhappen.ThoughんWikiの記事は次のように私が出会ったのと同じのための素晴らしい説明があるスプリアスウェイクアップについての良い取引を言及します-

考えてみてください...他のコードと同様に、スレッドスケジューラは、基盤となるハードウェア/ソフトウェアで異常が発生したために一時的にブラックアウトする可能性があります。もちろん、これができる限り発生しないように注意する必要がありますが、100%堅牢なソフトウェアなどは存在しないため、これが発生する可能性があると想定し、スケジューラがこれを検出した場合に備えて、正常な回復に注意する必要があります(たとえば、欠落した心拍を観察することにより)。

さて、ブラックアウト中に、待機中のスレッドに通知することを目的としたいくつかの信号を見逃す可能性があることを考慮して、スケジューラはどのように回復できますか?スケジューラが何もしない場合、言及された「不運な」スレッドはハングし、永久に待機します。これを回避するには、スケジューラは待機中のすべてのスレッドにシグナルを送信します。

これにより、理由なく待機中のスレッドに通知できる「契約」を確立する必要があります。正確には、理由があります-スケジューラーのブラックアウト-スレッドは(正当な理由で)スケジューラーの内部実装の詳細に気付かないように設計されているので、この理由は「偽物」として提示する方が良いでしょう。

私はソースからこの回答を読んでいて、十分に合理的であるとわかりました。また読む

Javaでの偽のウェイクアップとその回避方法

PS:上記のリンクは、偽のウェイクアップに関する詳細が記載されている私の個人ブログへのリンクです。


9

Cameron Purdyが、誤ったウェイクアップ問題に見舞われたことについて、しばらく前にブログ投稿を書きました。そう、それは起こります

Javaがデプロイされるプラットフォームの一部に制限があるため、(可能性として)仕様にあると思いますか?私は間違っているかもしれませんが!


私はこの投稿を読み、1つのアプリケーションのループ待機パラダイムへの適合をランダムまたは確定的に起動することによってテストするための単体テストを持つことについてのアイデアを与えました。それともすでにどこかで入手可能ですか?
akarnokd 2009年

それはSOに関する別の質問です:「テストに使用できる厳密な VM はありますか?」。私は厳格なスレッドローカルメモリと1を見てみたい-私は、彼らがまだ存在しないと思う
oxbow_lakes

8

これを追加するだけです。はい、発生しました。24コアマシン(JDK 6)でマルチスレッドの問題の原因を探すために3日間を費やしました。10の実行のうち4つはパターンなしでそれを経験しました。これは2コアまたは8コアでは発生しませんでした。

いくつかのオンライン資料を調査しましたが、これはJavaの問題ではなく、一般的なまれですが予期される動作です。


こんにちはルネス、あなたはそこで実行されているアプリを開発していますか?java docdocs.oracle.com/javase/6/docs/api/java/lang/…で推奨されているように、外部条件をループチェックしながらwhile()メソッドを呼び出すwait()メソッドがありますか?
ガムキン

私はそれについて書きました、そしてはい、解決策は条件チェックを伴うwhileループです。私の間違いはループの欠落でした...しかし、私はこれらのウェイクアップについて学びました... 2コアでは決して、しばしば24コアではblog.xceptance.com/2011/05/06/spurious-wakeup-the-rare-event
ReneS

40以上のコアUNIXサーバーでアプリケーションを実行したときも、同様の経験がありました。非常に多くの偽のウェイクアップがありました。-したがって、偽のウェイクアップの量はシステムのプロセッサコアの量に正比例するように見えます。
bvdb

0

https://stackoverflow.com/a/1461956/14731には、基盤となるオペレーティングシステムによってトリガーされない場合でも、偽のウェイクアップから保護する必要がある理由についての優れた説明が含まれています。この説明は、Javaを含む複数のプログラミング言語に適用されることに注意してください。


0

OPの質問に答える

ランダムなイベントを永遠に待たずに、この待機を誤って起動するために私は何ができますか?

偽のウェイクアップがこの待機中のスレッドをウェイクアップすることません

特定のプラットフォームで偽のウェイクアップが発生するかどうかに関係なく、OPのスニペットの場合、それは明らかに不可能です。のためにCondition.await()返すようにしてラインを参照するには、「スプリアスウェイクアップを!」出力ストリーム内。

あなたが非常にエキゾチックなものを使用しているのでない限り Javaクラスライブラリ

これは、標準的なためであるOpenJDKのさんReentrantLockの方法は、newCondition()返しAbstractQueuedSynchronizerののを実装Conditionネストインタフェース、ConditionObject(方法によって、それが唯一の実装でありCondition、このクラスライブラリのインターフェイス)、及びConditionObjectの方法await()条件がないかどうか自体をチェックします保持し、偽のウェイクアップによってこのメソッドが誤って戻ることを強制することはできません。

ちなみに、AbstractQueuedSynchronizerベースの実装が含まれていると、偽のウェイクアップをエミュレートするのは非常に簡単なので、自分で確認できます。 AbstractQueuedSynchronizer低レベルLockSupportparkunparkメソッドを使用します。LockSupport.unparkしているスレッドCondition、このアクションは偽のウェイクアップと区別できません。

OPのスニペットを少しリファクタリングし、

public class Spurious {

    private static class AwaitingThread extends Thread {

        @Override
        public void run() {
            Lock lock = new ReentrantLock();
            Condition cond = lock.newCondition();
            lock.lock();
            try {
                try {
                    cond.await();
                    System.out.println("Spurious wakeup!");
                } catch (InterruptedException ex) {
                    System.out.println("Just a regular interrupt.");
                }
            } finally {
                lock.unlock();
            }
        }
    }

    private static final int AMOUNT_OF_SPURIOUS_WAKEUPS = 10;

    public static void main(String[] args) throws InterruptedException {
        Thread awaitingThread = new AwaitingThread();
        awaitingThread.start();
        Thread.sleep(10000);
        for(int i =0 ; i < AMOUNT_OF_SPURIOUS_WAKEUPS; i++)
            LockSupport.unpark(awaitingThread);
        Thread.sleep(10000);
        if (awaitingThread.isAlive())
            System.out.println("Even after " + AMOUNT_OF_SPURIOUS_WAKEUPS + " \"spurious wakeups\" the Condition is stil awaiting");
        else
            System.out.println("You are using very unusual implementation of java.util.concurrent.locks.Condition");
    }
}

、およびunparking(main)スレッドが待機中のスレッドを起こそうとするのがどれほど困難であってCondition.await()も、この場合、メソッドは決して戻りません。

待機中のメソッドに対する誤ったウェイクアップConditionについては、インターフェースjavadocでCondition説明されています。とはいえ、

条件を待っているときに、偽のウェイクアップが発生することが許可されます

そしてそれ

アプリケーションプログラマーは常に発生する可能性があると想定し、常にループで待機することをお勧めします。

しかし、それは後でそれを追加します

実装は、偽のウェイクアップの可能性を自由に削除できます

およびAbstractQueuedSynchronizerConditionインターフェースの実装はまさにそれを行います- 偽のウェイクアップの可能性を排除します

これは確かに他にも当てはまります ConditionObjectのの待機中のメソッドにます。

だから、結論は次のとおりです。

常にCondition.awaitループで呼び出し、条件が満たされていないかどうかを確認する必要がありますが、標準のOpenJDKでは、Javaクラスライブラリが発生することありません。繰り返しになりますが、非常に珍しいJavaクラスライブラリを使用しない限り(これは非常に珍しいはずです。現在、ほとんど絶滅している別の有名な非OpenJDK Javaクラスライブラリは、GNU ClasspathApache HarmonyでありConditionインターフェースの標準実装と同一であるようです)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.