あなたが参照した元の質問 (ウィキペディアの記事の限り) は、プロセスがシグナルされる副作用として、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 になるためです。 セット