อะไรคือความเสียหายที่อาจเกิดขึ้นหากเป็นไปได้ที่จะเรียกใช้wait()
นอกบล็อกที่ซิงโครไนซ์โดยรักษาความหมายไว้ - ระงับเธรดผู้โทร?
เราจะแสดงให้เห็นถึงปัญหาที่เราจะพบหากwait()
สามารถเรียกใช้นอกบล็อกที่ซิงโครไนซ์ด้วยตัวอย่างที่เป็นรูปธรรมตัวอย่างที่เป็นรูปธรรม
สมมติว่าเราใช้คิวการบล็อก (ฉันรู้ว่ามีอยู่แล้วใน API :)
ความพยายามครั้งแรก (โดยไม่มีการซิงโครไนซ์) อาจดูบางสิ่งตามบรรทัดด้านล่าง
class BlockingQueue {
Queue<String> buffer = new LinkedList<String>();
public void give(String data) {
buffer.add(data);
notify(); // Since someone may be waiting in take!
}
public String take() throws InterruptedException {
while (buffer.isEmpty()) // don't use "if" due to spurious wakeups.
wait();
return buffer.remove();
}
}
นี่คือสิ่งที่อาจเกิดขึ้น:
ด้ายผู้บริโภคเรียกร้องและเห็นว่าtake()
buffer.isEmpty()
ก่อนที่เธรดผู้บริโภคจะดำเนินการต่อไปwait()
เธรดผู้ผลิตจะเข้ามาและเรียกใช้งานแบบเต็มgive()
นั่นคือbuffer.add(data); notify();
ด้ายของผู้บริโภคในขณะนี้จะเรียกwait()
(และพลาดnotify()
ที่ถูกเรียกว่าเพียงแค่)
หากโชคร้ายเธรดผู้ผลิตจะไม่ผลิตมากขึ้นgive()
เนื่องจากข้อเท็จจริงที่ว่าเธรดผู้บริโภคไม่เคยตื่นขึ้นมาและเรามีระบบล็อคแบบตาย
เมื่อคุณเข้าใจปัญหาแล้ววิธีแก้ไขก็ชัดเจน: ใช้synchronized
เพื่อให้แน่ใจว่าnotify
จะไม่ถูกเรียกระหว่างisEmpty
และwait
และ
โดยไม่ต้องลงรายละเอียด: ปัญหาการประสานนี้เป็นสากล ดังที่ Michael Borgwardt ชี้ให้เห็นการรอ / แจ้งนั้นเป็นเรื่องเกี่ยวกับการสื่อสารระหว่างเธรดดังนั้นคุณจะต้องจบลงด้วยสภาพการแข่งขันคล้ายกับที่อธิบายไว้ข้างต้น นี่คือเหตุผลที่มีการบังคับใช้กฎ "รอเฉพาะภายในที่ซิงโครไนซ์" เท่านั้น
ย่อหน้าจากลิงก์ที่โพสต์โดย @Willieสรุปว่าค่อนข้างดี:
คุณต้องการการรับประกันที่แน่นอนว่าบริกรและผู้แจ้งเตือนเห็นด้วยเกี่ยวกับสถานะของเพรดิเคต บริกรตรวจสอบสถานะของเพรดิเคตในบางจุดเล็กน้อยก่อนที่จะเข้าสู่โหมดสลีป แต่มันขึ้นอยู่กับความถูกต้องของเพรดิเคตที่เป็นจริงเมื่อมันเข้าสู่โหมดสลีป มีช่องโหว่ระหว่างสองเหตุการณ์ซึ่งอาจทำให้โปรแกรมเสียหายได้
buffer.isEmpty()
คำกริยาที่ผู้ผลิตและผู้บริโภคจำเป็นต้องตกลงอยู่ในตัวอย่างข้างต้น และข้อตกลงดังกล่าวได้รับการแก้ไขโดยการทำให้มั่นใจว่าการรอและการแจ้งเตือนนั้นดำเนินการเป็นsynchronized
บล็อก
โพสต์นี้ถูกเขียนใหม่เป็นบทความที่นี่: Java: เหตุใดจึงต้องรอในบล็อกที่ซิงค์