การเรียก IllegalMonitorStateException เมื่อรอ ()


162

ฉันใช้มัลติเธรดใน java สำหรับโปรแกรมของฉัน ฉันได้ทำงานด้ายประสบความสำเร็จ แต่เมื่อฉันใช้มันมีการขว้างปาThread.wait() java.lang.IllegalMonitorStateExceptionฉันจะทำให้เธรดรอจนกว่าจะได้รับการแจ้งเตือนได้อย่างไร


2
Thread.wait () ไม่มีอยู่อาจเป็น this.wait ()
Premraj

คำตอบ:


175

คุณต้องอยู่ในsynchronizedบล็อกก่อนจึงObject.wait()จะสามารถทำงานได้

นอกจากนี้ฉันขอแนะนำให้ดูแพ็คเกจการทำงานพร้อมกันแทนแพ็คเกจเธรดโรงเรียนเก่า พวกเขามีความปลอดภัยและวิธีการที่ง่ายต่อการทำงานร่วมกับ

การเข้ารหัสที่มีความสุข

แก้ไข

ฉันสันนิษฐานว่าคุณหมายถึงObject.wait()ข้อยกเว้นของคุณคือสิ่งที่เกิดขึ้นเมื่อคุณพยายามเข้าถึงโดยไม่ต้องถือล็อควัตถุ


1
จับดี. ฉันคิดว่าเขาหมายถึง Object.wait () และเรียกจากเธรด
reccles

2
บล็อกที่ซิงโครไนซ์บนวัตถุที่คุณรออยู่ สนใจที่จะแก้ไขคำตอบนี้เพื่อให้ชัดเจนขึ้นหรือไม่ ขอบคุณ
สีเทา

55

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โครงสร้างข้อมูลทุกครั้งที่ทำได้


5
ฉันไม่เคยเข้าใจวิธีการทำงานเนื่องจากการรอและการแจ้งเตือนนั้นอยู่ในบล็อกที่ซิงโครไนซ์กับวัตถุเดียวกัน (ล็อค) เนื่องจากเธรดรออยู่ในบล็อกจึงไม่ควรทำให้บล็อกเธรดแจ้งเตือนบนบรรทัด "ซิงโครไนซ์ (ล็อก)" ใช่หรือไม่
Brent212

6
@ Brent212 สำหรับวิธีการอื่น ๆ กว่าใด ๆใช่คุณไม่เคยได้รับไปwait notifyอย่างไรก็ตามใน API เอกสารสำหรับObject.wait"เธรดจะเผยแพร่ความเป็นเจ้าของของจอภาพนี้" ดังนั้นในขณะที่waitมันราวกับว่ามันอยู่นอกsynchronizedบล็อกล้อมรอบ(สำหรับวัตถุเดียวกันอาจเป็นหลายsynchronizedบล็อกในวัตถุเดียวกัน)
Tom Hawtin - tackline

24

ฉันรู้ว่ากระทู้นี้มีอายุเกือบ 2 ปี แต่ยังคงต้องปิดเพราะฉันยังได้เข้าสู่เซสชัน Q / A ที่มีปัญหาเดียวกัน ...

โปรดอ่านคำจำกัดความของการตรวจสอบที่ผิดกฎหมายนี้ซ้ำแล้วซ้ำอีก ...

IllegalMonitorException ถูกส่งออกมาเพื่อระบุว่าเธรดพยายามรอจอภาพของวัตถุหรือแจ้งเตือนเธรดอื่น ๆ ที่รอบนจอภาพของวัตถุโดยไม่ได้เป็นเจ้าของจอภาพที่ระบุ

บรรทัดนี้บอกอีกครั้งและอีกครั้ง IllegalMonitorException เกิดขึ้นเมื่อหนึ่งใน 2 สถานการณ์เกิดขึ้น ....

1> รอบนจอภาพของวัตถุโดยไม่ได้เป็นเจ้าของจอภาพที่ระบุ

2> แจ้งกระทู้อื่น ๆ ที่รอบนจอภาพของวัตถุโดยไม่ได้เป็นเจ้าของจอภาพที่ระบุ

บางคนอาจมีคำตอบของพวกเขา ... ใครไม่เป็นเช่นนั้นโปรดตรวจสอบ 2 ข้อความ ....

ทำข้อมูลให้ตรงกัน (วัตถุ)

object.wait ()

ถ้าวัตถุทั้งคู่เหมือนกัน ... จะไม่มีการตรวจสอบผิดกฎหมาย

อ่านคำนิยาม IllegalMonitorException อีกครั้งแล้วคุณจะไม่ลืมอีกต่อไป ...


จริงๆแล้วมันใช้งานไม่ได้ ฉันได้ลองแล้ว ฉันสร้าง Runnable ล็อคมัน (โดยใช้ซิงโครไนซ์บล็อก) และภายในบล็อกนั้นฉันรัน Runnable บน UI ของเธรด (Android) และหลังจากนั้นฉันก็ทำ myRunnable.wait () และฉันก็ยังได้รับข้อยกเว้น
เท็ด

คำอธิบายที่ยอดเยี่ยม !! ฉันกำลังรอ () โดยไม่ระบุวัตถุดังนั้นจึงเป็นตัวอย่างและประสานกับวัตถุอื่น ตอนนี้ฉันใช้ otherObject.wait () และมันใช้งานได้!
Fersca

6

จากความคิดเห็นของคุณดูเหมือนว่าคุณกำลังทำสิ่งนี้:

Thread thread = new Thread(new Runnable(){
    public void run() { // do stuff }});

thread.start();
...
thread.wait();

มีสามปัญหา

  1. อย่างที่คนอื่น ๆ พูดกันobj.wait()สามารถเรียกได้ว่าถ้าเธรดปัจจุบันเก็บล็อคดั้งเดิม / mutex objไว้ หากเธรดปัจจุบันไม่ได้ล็อคคุณจะได้รับข้อยกเว้นที่คุณเห็น

  2. การthread.wait()โทรไม่ได้ทำในสิ่งที่คุณคาดหวังว่าจะทำ โดยเฉพาะthread.wait() ไม่ได้ก่อให้เกิดการเสนอชื่อเข้าชิงด้ายที่จะรอ แต่มันทำให้เกิดเธรดปัจจุบันจะรอจนกว่าบางหัวข้ออื่น ๆ เรียกร้องหรือthread.notify()thread.notifyAll()

    จริงๆแล้วไม่มีวิธีที่ปลอดภัยในการบังคับให้Threadอินสแตนซ์หยุดชั่วคราวหากไม่ต้องการ (วิธีที่ใกล้ที่สุดที่ Java มีคือThread.suspend()วิธีเลิกใช้แต่วิธีนั้นไม่ปลอดภัยโดยเนื้อแท้ตามที่อธิบายใน Javadoc)

    หากคุณต้องการเริ่มต้นใหม่Threadเพื่อหยุดชั่วคราววิธีที่ดีที่สุดที่จะทำคือการสร้างCountdownLatchอินสแตนซ์และมีการเรียกเธรดawait()บนสลักเพื่อหยุดตัวเอง เธรดหลักจะเรียกcountDown()ใช้ latch เพื่อให้เธรดที่หยุดชั่วคราวดำเนินการต่อ

  3. มุมฉากไปยังจุดก่อนหน้าการใช้Threadวัตถุเป็นล็อค / mutex อาจทำให้เกิดปัญหา ตัวอย่างเช่น javadoc for Thread::joinพูดว่า:

    การดำเนินการนี้จะใช้วงของสายปรับอากาศบนthis.wait this.isAliveในฐานะที่เป็นกระทู้ยกเลิกthis.notifyAllวิธีการที่ถูกเรียก ก็จะแนะนำว่าการใช้งานที่ไม่ได้ใช้wait, notifyหรือnotifyAllในThreadกรณี


2

เนื่องจากคุณไม่ได้โพสต์รหัสเราจึงทำงานในที่มืด รายละเอียดของข้อยกเว้นมีอะไรบ้าง

คุณกำลังเรียก 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) { }

    }

@CPerkins: wait()ฉันคิดว่าคุณกำลังสับสนด้ายการดำเนินการและวัตถุซึ่งเป็นเป้าหมายของ
Robert Munteanu

@ Robert - บางทีฉันอาจเป็น แต่ฉันไม่คิดอย่างนั้น หากคุณเริ่มต้นอินสแตนซ์เธรดจากนั้นให้รอจนกว่าคุณจะได้รับ IllegalMonitorStateException ซึ่งเป็นสิ่งที่ฉันพยายามอธิบาย
CPerkins

คุณกำลังพูดถึงworker.wait()สาย? จากนั้นคุณควรซิงโครไนซ์กับคนงานไม่ใช่ล็อค
Robert Munteanu

1

เพื่อที่จะจัดการกับ 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();
        }
    }
}

0

Thread.wait () การเรียกใช้ควรใช้โค้ดที่ซิงโครไนซ์กับวัตถุ Thread.class ฉันไม่คิดว่ามันเป็นสิ่งที่คุณหมายถึง
คุณถาม

ฉันจะทำให้เธรดรอจนกว่าจะได้รับการแจ้งเตือนได้อย่างไร

คุณสามารถรอเธรดปัจจุบันของคุณเท่านั้น ด้ายอื่น ๆ สามารถขอให้เบา ๆ รอถ้ามันเห็นด้วย
หากคุณต้องการรอเงื่อนไขบางอย่างคุณต้องล็อควัตถุ - วัตถุ Thread.class เป็นตัวเลือกที่แย่มาก - เป็น AFAIK แบบซิงเกิลดังนั้นการซิงโครไนซ์กับมัน (ยกเว้นสำหรับวิธีการคงที่ Thread) เป็นอันตราย
รายละเอียดของการซิงโครไนซ์และการรอคอยถูกอธิบายโดย Tom Hawtin java.lang.IllegalMonitorStateExceptionหมายความว่าคุณกำลังพยายามรอวัตถุที่คุณไม่ได้ซิงโครไนซ์ซึ่งผิดกฎหมาย


0

ไม่แน่ใจว่าจะช่วยคนอื่นได้หรือไม่ แต่นี่เป็นส่วนสำคัญในการแก้ไขปัญหาของฉันในผู้ใช้ "Tom Hawtin - tacklin" คำตอบของด้านบน:

synchronized (lock) {
    makeWakeupNeeded();
    lock.notifyAll();
}

ความจริงที่ว่า "ล็อค" ถูกส่งผ่านเป็นอาร์กิวเมนต์ในการซิงโครไนซ์ () และยังใช้ใน "ล็อค" .notifyAll ();

เมื่อฉันทำมันในสถานที่ทั้งสองนี้ฉันได้ทำงาน


0

ฉันได้รับIllegalMonitorStateExceptionในขณะที่พยายามปลุกเธรดใน / จากclassเธรดอื่น ในjava 8คุณสามารถใช้lockคุณสมบัติของ Concurrency API ใหม่ แทนของsynchronizedฟังก์ชั่น

ฉันถูกแล้วการจัดเก็บวัตถุสำหรับการทำธุรกรรมในasynchronous WebSocket WeakHashMapทางออกในกรณีของฉันคือการจัดเก็บlockวัตถุในConcurrentHashMapเพื่อการsynchronousตอบกลับ หมายเหตุcondition.await (ไม่ได้.wait)

ที่จะจัดการกับหลายเธรดผมใช้Executors.newCachedThreadPool()ในการสร้างสระว่ายน้ำด้าย


0

ผู้ที่ใช้ 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
    }

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.