ฉันใช้มัลติเธรดใน java สำหรับโปรแกรมของฉัน ฉันได้ทำงานด้ายประสบความสำเร็จ แต่เมื่อฉันใช้มันมีการขว้างปาThread.wait()
java.lang.IllegalMonitorStateException
ฉันจะทำให้เธรดรอจนกว่าจะได้รับการแจ้งเตือนได้อย่างไร
ฉันใช้มัลติเธรดใน java สำหรับโปรแกรมของฉัน ฉันได้ทำงานด้ายประสบความสำเร็จ แต่เมื่อฉันใช้มันมีการขว้างปาThread.wait()
java.lang.IllegalMonitorStateException
ฉันจะทำให้เธรดรอจนกว่าจะได้รับการแจ้งเตือนได้อย่างไร
คำตอบ:
คุณต้องอยู่ในsynchronized
บล็อกก่อนจึงObject.wait()
จะสามารถทำงานได้
นอกจากนี้ฉันขอแนะนำให้ดูแพ็คเกจการทำงานพร้อมกันแทนแพ็คเกจเธรดโรงเรียนเก่า พวกเขามีความปลอดภัยและวิธีการที่ง่ายต่อการทำงานร่วมกับ
การเข้ารหัสที่มีความสุข
แก้ไข
ฉันสันนิษฐานว่าคุณหมายถึงObject.wait()
ข้อยกเว้นของคุณคือสิ่งที่เกิดขึ้นเมื่อคุณพยายามเข้าถึงโดยไม่ต้องถือล็อควัตถุ
wait
ถูกกำหนดไว้ในและไม่มันObject
Thread
จอภาพเปิดThread
ใช้งานไม่แน่นอน
แม้ว่าวัตถุ Java ทั้งหมดจะมีจอภาพ แต่โดยทั่วไปจะดีกว่าถ้ามีการล็อกเฉพาะ:
private final Object lock = new Object();
คุณสามารถอ่านการวินิจฉัยได้ง่ายขึ้นเล็กน้อยในราคาหน่วยความจำขนาดเล็ก (ประมาณ 2K ต่อกระบวนการ) โดยใช้คลาสที่มีชื่อ:
private static final class Lock { }
private final Object lock = new Lock();
เพื่อที่จะได้wait
หรือnotify
/ notifyAll
วัตถุคุณจะต้องมีการล็อกไว้กับsynchronized
คำสั่ง นอกจากนี้คุณจะต้องwhile
วนซ้ำเพื่อตรวจสอบเงื่อนไขการปลุก (หาข้อความที่ดีในการทำเกลียวเพื่ออธิบายสาเหตุ)
synchronized (lock) {
while (!isWakeupNeeded()) {
lock.wait();
}
}
วิธีแจ้งเตือน:
synchronized (lock) {
makeWakeupNeeded();
lock.notifyAll();
}
การทำความเข้าใจทั้งภาษาจาวาและjava.util.concurrent.locks
ล็อก (และjava.util.concurrent.atomic
) เป็นเรื่องที่คุ้มค่าเมื่อเข้าสู่มัลติเธรด แต่ใช้java.util.concurrent
โครงสร้างข้อมูลทุกครั้งที่ทำได้
wait
notify
อย่างไรก็ตามใน API เอกสารสำหรับObject.wait
"เธรดจะเผยแพร่ความเป็นเจ้าของของจอภาพนี้" ดังนั้นในขณะที่wait
มันราวกับว่ามันอยู่นอกsynchronized
บล็อกล้อมรอบ(สำหรับวัตถุเดียวกันอาจเป็นหลายsynchronized
บล็อกในวัตถุเดียวกัน)
ฉันรู้ว่ากระทู้นี้มีอายุเกือบ 2 ปี แต่ยังคงต้องปิดเพราะฉันยังได้เข้าสู่เซสชัน Q / A ที่มีปัญหาเดียวกัน ...
โปรดอ่านคำจำกัดความของการตรวจสอบที่ผิดกฎหมายนี้ซ้ำแล้วซ้ำอีก ...
IllegalMonitorException ถูกส่งออกมาเพื่อระบุว่าเธรดพยายามรอจอภาพของวัตถุหรือแจ้งเตือนเธรดอื่น ๆ ที่รอบนจอภาพของวัตถุโดยไม่ได้เป็นเจ้าของจอภาพที่ระบุ
บรรทัดนี้บอกอีกครั้งและอีกครั้ง IllegalMonitorException เกิดขึ้นเมื่อหนึ่งใน 2 สถานการณ์เกิดขึ้น ....
1> รอบนจอภาพของวัตถุโดยไม่ได้เป็นเจ้าของจอภาพที่ระบุ
2> แจ้งกระทู้อื่น ๆ ที่รอบนจอภาพของวัตถุโดยไม่ได้เป็นเจ้าของจอภาพที่ระบุ
บางคนอาจมีคำตอบของพวกเขา ... ใครไม่เป็นเช่นนั้นโปรดตรวจสอบ 2 ข้อความ ....
ทำข้อมูลให้ตรงกัน (วัตถุ)
object.wait ()
ถ้าวัตถุทั้งคู่เหมือนกัน ... จะไม่มีการตรวจสอบผิดกฎหมาย
อ่านคำนิยาม IllegalMonitorException อีกครั้งแล้วคุณจะไม่ลืมอีกต่อไป ...
จากความคิดเห็นของคุณดูเหมือนว่าคุณกำลังทำสิ่งนี้:
Thread thread = new Thread(new Runnable(){
public void run() { // do stuff }});
thread.start();
...
thread.wait();
มีสามปัญหา
อย่างที่คนอื่น ๆ พูดกันobj.wait()
สามารถเรียกได้ว่าถ้าเธรดปัจจุบันเก็บล็อคดั้งเดิม / mutex obj
ไว้ หากเธรดปัจจุบันไม่ได้ล็อคคุณจะได้รับข้อยกเว้นที่คุณเห็น
การthread.wait()
โทรไม่ได้ทำในสิ่งที่คุณคาดหวังว่าจะทำ โดยเฉพาะthread.wait()
ไม่ได้ก่อให้เกิดการเสนอชื่อเข้าชิงด้ายที่จะรอ แต่มันทำให้เกิดเธรดปัจจุบันจะรอจนกว่าบางหัวข้ออื่น ๆ เรียกร้องหรือthread.notify()
thread.notifyAll()
จริงๆแล้วไม่มีวิธีที่ปลอดภัยในการบังคับให้Thread
อินสแตนซ์หยุดชั่วคราวหากไม่ต้องการ (วิธีที่ใกล้ที่สุดที่ Java มีคือThread.suspend()
วิธีเลิกใช้แต่วิธีนั้นไม่ปลอดภัยโดยเนื้อแท้ตามที่อธิบายใน Javadoc)
หากคุณต้องการเริ่มต้นใหม่Thread
เพื่อหยุดชั่วคราววิธีที่ดีที่สุดที่จะทำคือการสร้างCountdownLatch
อินสแตนซ์และมีการเรียกเธรดawait()
บนสลักเพื่อหยุดตัวเอง เธรดหลักจะเรียกcountDown()
ใช้ latch เพื่อให้เธรดที่หยุดชั่วคราวดำเนินการต่อ
มุมฉากไปยังจุดก่อนหน้าการใช้Thread
วัตถุเป็นล็อค / mutex อาจทำให้เกิดปัญหา ตัวอย่างเช่น javadoc for Thread::join
พูดว่า:
การดำเนินการนี้จะใช้วงของสายปรับอากาศบน
this.wait
this.isAlive
ในฐานะที่เป็นกระทู้ยกเลิกthis.notifyAll
วิธีการที่ถูกเรียก ก็จะแนะนำว่าการใช้งานที่ไม่ได้ใช้wait
,notify
หรือnotifyAll
ในThread
กรณี
เนื่องจากคุณไม่ได้โพสต์รหัสเราจึงทำงานในที่มืด รายละเอียดของข้อยกเว้นมีอะไรบ้าง
คุณกำลังเรียก Thread.wait () จากภายในเธรดหรือภายนอก
ฉันถามสิ่งนี้เพราะตาม javadoc สำหรับ IllegalMonitorStateException มันเป็น:
โยนเพื่อระบุว่าเธรดพยายามรอจอภาพของวัตถุหรือแจ้งเตือนเธรดอื่น ๆ ที่รอบนจอภาพของวัตถุโดยไม่ได้เป็นเจ้าของจอภาพที่ระบุ
เพื่อชี้แจงคำตอบนี้การโทรนี้เพื่อรอเธรดยังส่ง IllegalMonitorStateException ไปด้วยแม้ว่าจะถูกเรียกจากภายในบล็อกที่ซิงโครไนซ์:
private static final class Lock { }
private final Object lock = new Lock();
@Test
public void testRun() {
ThreadWorker worker = new ThreadWorker();
System.out.println ("Starting worker");
worker.start();
System.out.println ("Worker started - telling it to wait");
try {
synchronized (lock) {
worker.wait();
}
} catch (InterruptedException e1) {
String msg = "InterruptedException: [" + e1.getLocalizedMessage() + "]";
System.out.println (msg);
e1.printStackTrace();
System.out.flush();
}
System.out.println ("Worker done waiting, we're now waiting for it by joining");
try {
worker.join();
} catch (InterruptedException ex) { }
}
wait()
ฉันคิดว่าคุณกำลังสับสนด้ายการดำเนินการและวัตถุซึ่งเป็นเป้าหมายของ
worker.wait()
สาย? จากนั้นคุณควรซิงโครไนซ์กับคนงานไม่ใช่ล็อค
เพื่อที่จะจัดการกับ IllegalMonitorStateException คุณต้องตรวจสอบว่าสวดทั้งหมดของการรอคอยแจ้งและวิธีการ notifyAll ที่เกิดขึ้นเฉพาะเมื่อมีการเรียกด้ายเป็นเจ้าของจอภาพที่เหมาะสม ทางออกที่ง่ายที่สุดคือใส่สายเหล่านี้ไว้ในบล็อคที่ซิงโครไนซ์ วัตถุการซิงโครไนซ์ที่จะถูกเรียกใช้ในคำสั่งการซิงโครไนซ์เป็นวัตถุที่ต้องได้รับการตรวจสอบ
นี่คือตัวอย่างง่าย ๆ สำหรับการทำความเข้าใจแนวคิดของจอภาพ
public class SimpleMonitorState {
public static void main(String args[]) throws InterruptedException {
SimpleMonitorState t = new SimpleMonitorState();
SimpleRunnable m = new SimpleRunnable(t);
Thread t1 = new Thread(m);
t1.start();
t.call();
}
public void call() throws InterruptedException {
synchronized (this) {
wait();
System.out.println("Single by Threads ");
}
}
}
class SimpleRunnable implements Runnable {
SimpleMonitorState t;
SimpleRunnable(SimpleMonitorState t) {
this.t = t;
}
@Override
public void run() {
try {
// Sleep
Thread.sleep(10000);
synchronized (this.t) {
this.t.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Thread.wait () การเรียกใช้ควรใช้โค้ดที่ซิงโครไนซ์กับวัตถุ Thread.class ฉันไม่คิดว่ามันเป็นสิ่งที่คุณหมายถึง
คุณถาม
ฉันจะทำให้เธรดรอจนกว่าจะได้รับการแจ้งเตือนได้อย่างไร
คุณสามารถรอเธรดปัจจุบันของคุณเท่านั้น ด้ายอื่น ๆ สามารถขอให้เบา ๆ รอถ้ามันเห็นด้วย
หากคุณต้องการรอเงื่อนไขบางอย่างคุณต้องล็อควัตถุ - วัตถุ Thread.class เป็นตัวเลือกที่แย่มาก - เป็น AFAIK แบบซิงเกิลดังนั้นการซิงโครไนซ์กับมัน (ยกเว้นสำหรับวิธีการคงที่ Thread) เป็นอันตราย
รายละเอียดของการซิงโครไนซ์และการรอคอยถูกอธิบายโดย Tom Hawtin
java.lang.IllegalMonitorStateException
หมายความว่าคุณกำลังพยายามรอวัตถุที่คุณไม่ได้ซิงโครไนซ์ซึ่งผิดกฎหมาย
ไม่แน่ใจว่าจะช่วยคนอื่นได้หรือไม่ แต่นี่เป็นส่วนสำคัญในการแก้ไขปัญหาของฉันในผู้ใช้ "Tom Hawtin - tacklin" คำตอบของด้านบน:
synchronized (lock) {
makeWakeupNeeded();
lock.notifyAll();
}
ความจริงที่ว่า "ล็อค" ถูกส่งผ่านเป็นอาร์กิวเมนต์ในการซิงโครไนซ์ () และยังใช้ใน "ล็อค" .notifyAll ();
เมื่อฉันทำมันในสถานที่ทั้งสองนี้ฉันได้ทำงาน
ฉันได้รับIllegalMonitorStateException
ในขณะที่พยายามปลุกเธรดใน / จากclass
เธรดอื่น ในjava 8
คุณสามารถใช้lock
คุณสมบัติของ Concurrency API ใหม่ แทนของsynchronized
ฟังก์ชั่น
ฉันถูกแล้วการจัดเก็บวัตถุสำหรับการทำธุรกรรมในasynchronous
WebSocket WeakHashMap
ทางออกในกรณีของฉันคือการจัดเก็บlock
วัตถุในConcurrentHashMap
เพื่อการsynchronous
ตอบกลับ หมายเหตุcondition.await
(ไม่ได้.wait
)
ที่จะจัดการกับหลายเธรดผมใช้Executors.newCachedThreadPool()
ในการสร้างสระว่ายน้ำด้าย
ผู้ที่ใช้ Java 7.0 หรือรุ่นต่ำกว่าสามารถอ้างอิงรหัสที่ฉันใช้ที่นี่และใช้งานได้
public class WaitTest {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
public void waitHere(long waitTime) {
System.out.println("wait started...");
lock.lock();
try {
condition.await(waitTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lock.unlock();
System.out.println("wait ends here...");
}
public static void main(String[] args) {
//Your Code
new WaitTest().waitHere(10);
//Your Code
}
}