IllegalMonitorStateException при ожидании () вызова - PullRequest
152 голосов
/ 08 октября 2009

Я использую многопоточность в Java для моей программы. Я успешно запустил поток, но когда я использую Thread.wait(), он выбрасывает java.lang.IllegalMonitorStateException. Как я могу заставить поток ждать, пока он не будет уведомлен?

Ответы [ 10 ]

163 голосов
/ 08 октября 2009

Вы должны быть в блоке synchronized, чтобы Object.wait() работал.

Кроме того, я рекомендую смотреть на пакеты параллелизма вместо пакетов потоков старой школы. Они безопаснее и с легче работать с *1006*.

Удачного кодирования.

EDIT

Я предположил, что вы имели в виду Object.wait(), поскольку ваше исключение происходит, когда вы пытаетесь получить доступ, не удерживая блокировку объектов.

50 голосов
/ 08 октября 2009

wait определяется в Object, а не в Thread. Монитор на Thread немного непредсказуем.

Хотя все объекты Java имеют мониторы, обычно лучше иметь выделенную блокировку:

private final Object lock = new Object();

С помощью именованного класса вы можете немного легче читать диагностику при небольших затратах памяти (около 2 КБ на процесс):

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, так и в java.util.concurrent.locks блокировках (и java.util.concurrent.atomic) при переходе в многопоточность. Но используйте java.util.concurrent структуры данных всякий раз, когда можете.

23 голосов
/ 25 января 2012

Я знаю, что этой ветке уже почти 2 года, но все еще нужно закрыть ее, так как я также пришел на этот сеанс вопросов и ответов с той же проблемой ...

Пожалуйста, прочитайте это определение invalidMonitorException снова и снова ...

IllegalMonitorException генерируется, чтобы указать, что поток пытался ждать на мониторе объекта или уведомлять другие потоки, ожидающие на мониторе объекта, не имея указанного монитора.

Эта строка снова и снова говорит, что IllegalMonitorException возникает, когда возникает одна из двух ситуаций ....

1> ждать на мониторе объекта, не имея указанного монитора.

2> уведомить другие потоки, ожидающие на мониторе объекта, не имея указанного монитора.

Кто-то, возможно, получил ответы ... а кто нет, тогда, пожалуйста, проверьте 2 утверждения ...

синхронизировано (объект)

Object.wait ()

Если оба объекта одинаковы ..., тогда не может быть никакого незаконного исключения.

Теперь снова прочитайте определение IllegalMonitorException, и вы не забудете его снова ...

5 голосов
/ 08 октября 2009

На основании ваших комментариев кажется, что вы делаете что-то вроде этого:

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

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

Есть три проблемы.

  1. Как уже говорили другие, obj.wait() может быть вызван, только если текущий поток содержит примитив блокировки / мьютекса для obj. Если текущий поток не удерживает блокировку, вы получаете исключение, которое вы видите.

  2. Вызов thread.wait() не делает того, чего вы ожидаете. В частности, thread.wait() не заставляет назначенный поток ждать. Скорее это заставляет текущий поток ждать, пока какой-то другой поток вызовет thread.notify() или thread.notifyAll().

    На самом деле нет безопасного способа заставить экземпляр Thread приостановить работу, если он этого не хочет. (Ближайший Java имеет к этому устаревший Thread.suspend() метод, но этот метод небезопасен, как объясняется в Javadoc.)

    Если вы хотите, чтобы только что запущенный Thread сделал паузу, лучший способ сделать это - создать экземпляр CountdownLatch и заставить поток вызывать await() в защелке, чтобы сделать паузу самостоятельно. Затем основной поток будет вызывать countDown() в защелке, чтобы продолжить приостановленную нить.

  3. Ортогонально предыдущим точкам, использование объекта Thread в качестве блокировки / мьютекса может вызвать проблемы. Например, Javadoc для Thread::join говорит:

    В этой реализации используется цикл вызовов this.wait, обусловленных this.isAlive. Когда поток завершается, вызывается метод this.notifyAll. Рекомендуется, чтобы приложения не использовали wait, notify или notifyAll в Thread экземплярах.

1 голос
/ 06 октября 2018

Чтобы иметь дело с IllegalMonitorStateException, вы должны убедиться, что все вызовы методов wait, notify и 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();
        }
    }
}
1 голос
/ 08 октября 2009

Поскольку вы не разместили код, мы вроде как работаем в темноте. Какие детали исключения?

Вы вызываете 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) { }

    }
0 голосов
/ 20 января 2018

Те, кто использует 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
    }

}
0 голосов
/ 19 января 2018

Я получил IllegalMonitorStateException при попытке разбудить поток в / из другого потока class /. В java 8 вы можете использовать lock функций нового Concurrency API вместо из synchronized функций.

Я уже хранил объекты для транзакций asynchronous websocket в WeakHashMap. Решением в моем случае было также сохранить lock объект в ConcurrentHashMap для synchronous ответов. Примечание condition.await (не .wait).

Для обработки многопоточности я использовал Executors.newCachedThreadPool() для создания пула потоков .

0 голосов
/ 10 октября 2014

Не уверен, поможет ли это кому-то другому или нет, но это было ключевым моментом, чтобы исправить мою проблему в ответе пользователя "Tom Hawtin - tacklin" выше:

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

Просто тот факт, что «lock» передается в качестве аргумента в synchronized () и также используется в «lock» .notifyAll ();

Как только я сделал это в тех двух местах, я получил его на работу

0 голосов
/ 08 октября 2009

Вызов Thread.wait () имеет смысл в коде, который синхронизируется с объектом Thread.class. Я не думаю, что это то, что вы имели в виду.
Вы спрашиваете

Как заставить поток ждать, пока он не будет уведомлен?

Вы можете подождать только вашу текущую тему. Любую другую ветку можно только осторожно попросить подождать, если она согласится.
Если вы хотите подождать какое-то условие, вам нужен объект блокировки - объект Thread.class является очень плохим выбором - это одноэлементный AFAIK, поэтому синхронизация на нем (за исключением статических методов Thread) опасна.
Подробности синхронизации и ожидания уже объясняются Томом Хоутином. java.lang.IllegalMonitorStateException означает, что вы пытаетесь дождаться объекта, с которым вы не синхронизированы - это незаконно.

...