การเรียก 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.