ตอบคำถามของ OP
ฉันจะทำอย่างไรเพื่อปลุกสิ่งนี้รออย่างไม่หยุดยั้งโดยไม่ต้องรอตลอดไปสำหรับเหตุการณ์สุ่ม
, ไม่มีใด ๆ ปลุกปลอมอาจจะตื่นขึ้นมานี้ด้ายรอ!
โดยไม่คำนึงว่า wakeups ปลอมสามารถหรือไม่สามารถเกิดขึ้นได้บนแพลตฟอร์มโดยเฉพาะอย่างยิ่งในกรณีของ OP ที่ snippet มันเป็นบวกเป็นไปไม่ได้สำหรับการCondition.await()
จะกลับมาและจะเห็นเส้น "ปลุกเก๊!" ในเอาต์พุตสตรีม
นอกจากว่าคุณกำลังใช้Java Class Library ที่แปลกใหม่มาก
เพราะนี่คือมาตรฐานOpenJDK 's ReentrantLock
' s วิธีการnewCondition()
ผลตอบแทนAbstractQueuedSynchronizer
ของการดำเนินงานของCondition
อินเตอร์เฟซที่ซ้อนกันConditionObject
(โดยวิธีการก็คือการดำเนินการเฉพาะของCondition
อินเตอร์เฟซในห้องสมุดชั้นนี้) และConditionObject
's วิธีการawait()
ของตัวเองการตรวจสอบว่าอยู่ในสภาพที่ไม่ได้ การถือครองและไม่มีการปลอมแปลงใด ๆ ที่ทำให้การปลุกผิดพลาดสามารถบังคับให้วิธีนี้กลับมาผิดพลาดได้
โดยวิธีการที่คุณสามารถตรวจสอบด้วยตัวคุณเองว่ามันเป็นเรื่องง่ายที่จะเลียนแบบการปลอมเมื่อตื่นนอนการAbstractQueuedSynchronizer
ดำเนินการตามฐานที่เกี่ยวข้อง
AbstractQueuedSynchronizer
ใช้ในระดับต่ำLockSupport
's park
และunpark
วิธีการและถ้าคุณเรียก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");
}
}
และไม่ว่าเธรด (หลัก) ที่ไม่มีการดึงข้อมูลจะพยายามปลุกเธรดที่กำลังรออยู่มากเพียงใดCondition.await()
เมธอดจะไม่ส่งคืนในกรณีนี้
wakeups ปลอมบนCondition
's วิธีการรอที่จะกล่าวถึงในJavadoc ของCondition
อินเตอร์เฟซ แม้ว่ามันจะบอกว่า
เมื่อรอเงื่อนไขจะอนุญาตให้มีการปลุกปลอมโดยเกิดขึ้น
และนั่น
ขอแนะนำให้โปรแกรมเมอร์แอปพลิเคชันคิดเสมอว่าสามารถเกิดขึ้นได้และรอเป็นวงอยู่เสมอ
แต่ต่อมามันก็เสริมว่า
การใช้งานมีอิสระที่จะลบความเป็นไปได้ของการปลุกปลอม
และAbstractQueuedSynchronizer
ของการดำเนินงานของCondition
อินเตอร์เฟซที่ไม่ตรงที่ - เอาไปได้ของการ wakeups
สิ่งนี้ถือเป็นจริงสำหรับConditionObject
วิธีการรอของผู้อื่น
ดังนั้นข้อสรุปคือ:
เราควรโทรCondition.await
เข้าไปในลูปเสมอและตรวจสอบว่าเงื่อนไขไม่ได้เก็บไว้ แต่ด้วยมาตรฐาน OpenJDK, Java Class Library จะไม่มีทางเกิดขึ้นได้ นอกจากนั้นคุณใช้ Java Class Library ที่ผิดปกติมาก (ซึ่งจะต้องผิดปกติมากเพราะไลบรารี Java Class อื่นที่ไม่ใช่ OpenJDK Java ที่รู้จักกันดีในปัจจุบันเกือบจะสูญพันธุ์GNU ClasspathและApache Harmonyดูเหมือนจะเหมือนกับการใช้มาตรฐานของCondition
อินเตอร์เฟส)
pthread_cond_wait()
คำถามจริงคือ"ทำไม pthread_cond_wait ถึงมีการปลุกที่ผิดพลาด" .