IllegalMonitorStateException при ожидании () вызова


163

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


2
Thread.wait () не существует, это может быть this.wait ()
Premraj

Ответы:


176

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

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

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

РЕДАКТИРОВАТЬ

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


1
хороший улов. я предположил, что он имел в виду Object.wait () и вызвал из потока
восходит

2
Синхронизированный блок на объекте, на котором вы ожидаете. Хотите отредактировать этот ответ, чтобы сделать его немного более понятным? Спасибо.
Серый

55

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структуры данных, когда вы можете.


5
Я никогда не понимал, как это работает, учитывая, что ожидание и уведомление находятся в синхронизированных блоках на одном и том же объекте (блокировка). Поскольку поток ожидания находится в блоке, разве это не должно блокировать поток уведомлений в строке "synchronized (lock)"?
Brent212

6
@ Brent212 Для любого метода, кроме wait, да, вы никогда не получите notify. Однако в документах API для Object.wait«Поток освобождает владельца этого монитора». Таким образом, в waitто время как в нем, как будто это находится за пределами вмещающих synchronizedблоков (для одного и того же объекта, может быть несколько synchronizedблоков на одном объекте).
Том Хотин -

24

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

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

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

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

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

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

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

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

Object.wait ()

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

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


На самом деле, это не работает. Я попробовал это. Я создаю Runnable, блокирую его (используя синхронизированный блок), и внутри этого блока я запускаю Runnable в UI-потоке (Android) и после этого выполняю myRunnable.wait (), и все равно получаю исключение.
Тед

Отличное объяснение !! Я делал wait () без указания объекта, поэтому он взял экземпляр и синхронизировал другой объект. Теперь я использую otherObject.wait (), и это работает!
Fersca

6

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

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случаях.


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().
Роберт Мунтяну

@ Роберт - Может быть, но я так не думаю. Если вы запустите экземпляр Thread, а затем попросите его подождать, вы получите исключение IllegalMonitorStateException, которое я и пытался описать.
CPerkins

Вы говорите о worker.wait()линии? Тогда вы должны синхронизироваться на рабочем, а не на блокировке.
Роберт Мунтяну

1

Чтобы иметь дело с 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();
        }
    }
}

0

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

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

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


0

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

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

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

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


0

Я получил некоторое IllegalMonitorStateExceptionвремя, пытаясь разбудить поток в / из другого class/ потока. В java 8вы можете использовать lockфункции нового параллелизма 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
    }

}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.