IllegalMonitorStateException khi chờ cuộc gọi ()


161

Tôi đang sử dụng đa luồng trong java cho chương trình của mình. Tôi đã chạy thread thành công nhưng khi tôi đang sử dụng Thread.wait(), nó đang ném java.lang.IllegalMonitorStateException. Làm thế nào tôi có thể làm cho một chủ đề chờ cho đến khi nó sẽ được thông báo?


2
Thread.wait () không tồn tại, nó có thể là this.wait ()
Premraj 24/07/2015

Câu trả lời:


174

Bạn cần phải ở trong một synchronizedkhối Object.wait()để làm việc.

Ngoài ra, tôi khuyên bạn nên xem xét các gói tương tranh thay vì các gói luồng cũ. Họ an toàn hơn và cách dễ dàng hơn để làm việc với .

Chúc mừng mã hóa.

BIÊN TẬP

Tôi giả sử bạn có nghĩa Object.wait()là ngoại lệ của bạn là những gì xảy ra khi bạn cố gắng truy cập mà không giữ khóa đối tượng.


1
nắm bắt tốt. tôi giả sử anh ta có nghĩa là Object.wait () và được gọi từ một chủ đề
thiệu

2
Một khối được đồng bộ hóa trên đối tượng mà bạn đang chờ đợi. Muốn chỉnh sửa câu trả lời này để làm cho nó rõ ràng hơn một chút? Cảm ơn.
Xám

55

waitđược định nghĩa trong Object, và không phải nó Thread. Màn hình trên Threadlà một chút khó lường.

Mặc dù tất cả các đối tượng Java đều có màn hình, nhưng tốt hơn hết là có khóa chuyên dụng:

private final Object lock = new Object();

Bạn có thể dễ dàng hơn để đọc chẩn đoán, với chi phí bộ nhớ nhỏ (khoảng 2K mỗi quy trình) bằng cách sử dụng một lớp được đặt tên:

private static final class Lock { }
private final Object lock = new Lock();

Để waithoặc notify/ notifyAllmột đối tượng, bạn cần giữ khóa với synchronizedcâu lệnh. Ngoài ra, bạn sẽ cần một whilevòng lặp để kiểm tra điều kiện đánh thức (tìm một văn bản hay về luồng để giải thích tại sao).

synchronized (lock) {
    while (!isWakeupNeeded()) {
        lock.wait();
    }
}

Thông báo:

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

Rất đáng để hiểu cả ngôn ngữ Java và các java.util.concurrent.lockskhóa (và java.util.concurrent.atomic) khi đi vào đa luồng. Nhưng sử dụng java.util.concurrentcấu trúc dữ liệu bất cứ khi nào bạn có thể.


5
Tôi chưa bao giờ hiểu làm thế nào điều này hoạt động, cho rằng chờ đợi và thông báo đều ở trong các khối được đồng bộ hóa trên cùng một đối tượng (khóa). Vì luồng chờ trong khối, không nên tạo khối luồng thông báo trên dòng "đã đồng bộ hóa (khóa)"?
Brent212

6
@ Brent212 Đối với bất kỳ phương pháp nào khác wait, vâng, bạn sẽ không bao giờ có được notify. Tuy nhiên, trong tài liệu API cho Object.wait"Chủ đề phát hành quyền sở hữu màn hình này". Vì vậy, trong khi waitnó ở bên ngoài các synchronizedkhối kèm theo (đối với cùng một đối tượng, có thể là nhiều synchronizedkhối trên cùng một đối tượng).
Tom Hawtin - tackline

24

Tôi biết chủ đề này đã gần 2 năm nhưng vẫn cần phải đóng chủ đề này vì tôi cũng đã đến phiên hỏi đáp này với cùng một vấn đề ...

Vui lòng đọc lại định nghĩa bất hợp phápMonitorException này ...

IllegalMonitorException được ném để chỉ ra rằng một luồng đã cố đợi trên màn hình của đối tượng hoặc thông báo cho các luồng khác đang chờ trên màn hình của đối tượng mà không sở hữu màn hình được chỉ định.

Dòng này lặp đi lặp lại, IllegalMonitorException xuất hiện khi một trong 2 tình huống xảy ra ....

1> chờ trên màn hình của đối tượng mà không cần sở hữu màn hình đã chỉ định.

2> thông báo cho các luồng khác đang chờ trên màn hình của đối tượng mà không cần sở hữu màn hình được chỉ định.

Một số có thể đã có câu trả lời của họ ... tất cả những người không, sau đó vui lòng kiểm tra 2 câu ....

đồng bộ hóa (đối tượng)

object.wait ()

Nếu cả hai đối tượng đều giống nhau ... thì không có trường hợp ngoại lệ bất hợp pháp nào có thể đến.

Bây giờ một lần nữa đọc định nghĩa IllegalMonitorException và bạn sẽ không quên nó một lần nữa ...


Trên thực tế, điều đó không làm việc. Tôi đã thử nó. Tôi tạo một Runnable, khóa nó (sử dụng khối được đồng bộ hóa) và bên trong khối đó tôi chạy Runnable trên UI-thread (Android) và sau đó tôi thực hiện myRunnable.wait () và tôi vẫn nhận được ngoại lệ.
Ted

Giải thích về Excelente !! Tôi đã thực hiện Wait () mà không chỉ định đối tượng, vì vậy nó đã lấy ví dụ và đồng bộ hóa trên một đối tượng khác. Bây giờ tôi đang sử dụng otherObject.wait () và nó hoạt động!
Fersca

6

Dựa trên ý kiến ​​của bạn, có vẻ như bạn đang làm một cái gì đó như thế này:

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

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

Có ba vấn đề.

  1. Như những người khác đã nói, obj.wait()chỉ có thể được gọi nếu luồng hiện tại giữ khóa / mutex nguyên thủy cho obj. Nếu luồng hiện tại không giữ khóa, bạn sẽ có ngoại lệ bạn đang thấy.

  2. Cuộc thread.wait()gọi không thực hiện những gì bạn dường như mong đợi. Cụ thể, thread.wait() không làm cho các chủ đề được đề cử để chờ đợi. Thay vào đó, nó làm cho luồng hiện tại chờ cho đến khi một số luồng khác gọi thread.notify()hoặc thread.notifyAll().

    Thực tế không có cách nào an toàn để buộc một Threadcá thể tạm dừng nếu nó không muốn. (Cách gần nhất mà Java có là Thread.suspend()phương thức không dùng nữa , nhưng phương thức đó vốn không an toàn, như được giải thích trong Javadoc.)

    Nếu bạn muốn Threadtạm dừng mới bắt đầu , cách tốt nhất để làm điều đó là tạo một CountdownLatchthể hiện và có lệnh gọi await()trên chốt để tạm dừng chính nó. Chủ đề chính sau đó sẽ gọi countDown()vào chốt để cho phép chủ đề tạm dừng tiếp tục.

  3. Trực giao với các điểm trước đó, sử dụng một Threadđối tượng làm khóa / mutex có thể gây ra vấn đề. Ví dụ: javadoc cho Thread::joinbiết:

    Việc thực hiện này sử dụng một vòng các this.waitcuộc gọi có điều kiện this.isAlive. Khi một luồng kết thúc, this.notifyAllphương thức được gọi. Đó là khuyến cáo rằng các ứng dụng không sử dụng wait, notifyhoặc notifyAllvào Threadtrường.


2

Vì bạn chưa đăng mã, chúng tôi làm việc trong bóng tối. Các chi tiết của ngoại lệ là gì?

Bạn đang gọi Thread.wait () từ bên trong chuỗi, hoặc bên ngoài nó?

Tôi hỏi điều này bởi vì theo javadoc cho IllegalMonitorStateException, đó là:

Ném để chỉ ra rằng một luồng đã cố đợi trên màn hình của đối tượng hoặc thông báo cho các luồng khác đang chờ trên màn hình của đối tượng mà không sở hữu màn hình được chỉ định.

Để làm rõ câu trả lời này, lệnh gọi chờ trên chuỗi này cũng ném IllegalMonitorStateException, mặc dù được gọi từ trong một khối được đồng bộ hóa:


     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: Tôi nghĩ bạn đang nhầm lẫn giữa luồng thực thi và đối tượng là mục tiêu của wait().
Robert Munteanu

@Robert - Có lẽ tôi, nhưng tôi không nghĩ vậy. Nếu bạn bắt đầu một phiên bản Thread và sau đó yêu cầu nó chờ, bạn sẽ nhận được IllegalMonitorStateException, đó là những gì tôi đang cố gắng mô tả.
CPerkins

Bạn đang nói về worker.wait()dòng? Sau đó, bạn nên đồng bộ hóa trên công nhân, không phải trên khóa.
Robert Munteanu

1

Để đối phó với IllegalMonitorStateException, bạn phải xác minh rằng tất cả các lệnh chờ, thông báo và thông báo Tất cả các phương thức chỉ diễn ra khi luồng gọi sở hữu màn hình phù hợp . Giải pháp đơn giản nhất là gửi các cuộc gọi này bên trong các khối được đồng bộ hóa. Đối tượng đồng bộ hóa sẽ được gọi trong câu lệnh được đồng bộ hóa là đối tượng mà màn hình phải được thu nhận.

Dưới đây là ví dụ đơn giản để hiểu khái niệm màn hình

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

Cuộc gọi Thread.wait () có ý nghĩa bên trong một mã được đồng bộ hóa trên đối tượng Thread. Class. Tôi không nghĩ đó là ý bạn.
Bạn hỏi

Làm thế nào tôi có thể làm cho một chủ đề chờ cho đến khi nó sẽ được thông báo?

Bạn chỉ có thể làm cho chủ đề hiện tại của bạn chờ đợi. Bất kỳ chủ đề khác có thể chỉ được yêu cầu nhẹ nhàng chờ đợi, nếu nó đồng ý.
Nếu bạn muốn đợi một số điều kiện, bạn cần một đối tượng khóa - Đối tượng Thread. Class là một lựa chọn rất tệ - đó là một AFAIK đơn lẻ nên việc đồng bộ hóa trên nó (ngoại trừ các phương thức tĩnh của Thread) rất nguy hiểm.
Chi tiết về đồng bộ hóa và chờ đợi đã được Tom Hawtin giải thích. java.lang.IllegalMonitorStateExceptioncó nghĩa là bạn đang cố gắng chờ đợi đối tượng mà bạn không được đồng bộ hóa - việc làm như vậy là bất hợp pháp.


0

Không chắc điều này có giúp được người khác hay không nhưng đây là phần quan trọng để khắc phục vấn đề của tôi trong câu trả lời của người dùng "Tom Hawtin - tacklin" ở trên:

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

Thực tế là "khóa" được truyền dưới dạng đối số được đồng bộ hóa () và nó cũng được sử dụng trong "khóa" .notify ALL ();

Khi tôi đã làm nó ở 2 nơi đó, tôi đã làm cho nó hoạt động


0

Tôi đã nhận được một IllegalMonitorStateExceptionlúc cố gắng đánh thức một chủ đề trong / từ một classchủ đề / khác . Trong java 8bạn có thể sử dụng các locktính năng của API đồng thời mới thay vì các synchronizedchức năng.

Tôi đã lưu trữ các đối tượng cho asynchronouscác giao dịch websocket trong một WeakHashMap. Giải pháp trong trường hợp của tôi là cũng lưu trữ một lockđối tượngConcurrentHashMap để synchronoustrả lời. Lưu ý các condition.await(không .wait).

Để xử lý đa luồng tôi đã sử dụng một Executors.newCachedThreadPool()để tạo một nhóm luồng .


0

Những người đang sử dụng phiên bản Java 7.0 trở xuống có thể tham khảo mã mà tôi đã sử dụng ở đây và nó hoạt động.

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
    }

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.