IllegalMonitorStateException при виклику wait ()


162

Я використовую для моєї програми багатопотокове передавання в java. Я успішно запустив нитку, але коли я використовую Thread.wait(), вона кидає java.lang.IllegalMonitorStateException. Як змусити нитку чекати, поки вона буде повідомлена?


2
Thread.wait () не існує, це може бути це.wait ()
Premraj

Відповіді:


175

Вам потрібно бути в synchronizedблоці, Object.wait()щоб працювати.

Також я рекомендую подивитися пакети одночасності замість старих шкільних пакетів для нарізки. З ними безпечніше і простіше працювати .

Щасливе кодування.

EDIT

Я припускав, що ви маєте на увазі 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або об’єкт, вам потрібно тримати замок із заявою. Крім того, вам знадобиться цикл, щоб перевірити стан пробудження (знайдіть хороший текст на нитці, щоб пояснити, чому).notifynotifyAllsynchronizedwhile

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

Повідомляти:

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

Варто розібратися в розумінні як мови Java, так і java.util.concurrent.locksблокувань (і java.util.concurrent.atomic) при вступі в багатопотоковість. Але використовуйте java.util.concurrentструктури даних, коли зможете.


5
Я ніколи не розумів, як це працює, враховуючи, що очікування та сповіщення знаходяться в синхронізованих блоках одного і того ж об’єкта (блокування). Оскільки потік очікування знаходиться в блоці, чи не повинен це зробити блок потоків сповіщень у рядку "синхронізований (замок)"?
Brent212

6
@ Brent212 Для будь-якого іншого способу, крім того wait, так ви ніколи не потрапите notify. Однак у документах API для Object.wait"Нитка звільняє право власності на цей монітор". Тож у той час wait, як ніби він знаходиться поза synchronizedблоками, що обгороджуються (для одного і того ж об'єкта можуть бути кілька synchronizedблоків на одному об'єкті).
Том Хотін - тайклін

24

Я знаю, що цій темі вже майже 2 роки, але все-таки потрібно закрити цю проблему, оскільки я також прийшов на цю сесію запитань з такою ж проблемою ...

Будь ласка, читайте це визначення незаконногоMonitorException знову і знову ...

IllegalMonitorException кидається, щоб вказати, що потік намагався чекати на моніторі об'єкта або сповіщати інші потоки, що очікують на моніторі об'єкта, не володіючи вказаним монітором.

Цей рядок знову і знову говорить, що IllegalMonitorException виникає, коли виникає одна з двох ситуацій ....

1> чекайте на моніторі об'єкта, не володіючи вказаним монітором.

2> повідомляти інші потоки, що очікують на моніторі об'єкта, не володіючи вказаним монітором.

Деякі, можливо, отримали свої відповіді ... хто все ні, тоді, будь ласка, перевірте 2 твердження ....

синхронізований (об'єкт)

object.wait ()

Якщо обидва об'єкти однакові ... тоді не може бути незаконнимMonitorException.

Тепер знову прочитайте визначення IllegalMonitorException, і ви його не забудете знову ...


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

Excelte пояснення !! Я робив wait (), не вказуючи об'єкт, тому він взяв екземпляр і синхронізував інший об'єкт. Зараз я використовую otherObject.wait (), і він працює!
Ферська

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 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 - Можливо, я є, але я не думаю, що так. Якщо ви запустите екземпляр Thread, а потім попросите його почекати, ви отримаєте IllegalMonitorStateException, про що я намагався описати.
CPerkins

Ви говорите про worker.wait()лінію? Тоді вам слід синхронізувати працівника, а не замку.
Роберт Мунтяну

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, тому синхронізація на ньому (за винятком статичних методів потоку) небезпечна.
Деталі синхронізації та очікування вже пояснив Том Хоутін. java.lang.IllegalMonitorStateExceptionозначає, що ви намагаєтесь чекати об’єкта, на якому ви не синхронізовані - це робити незаконно.


0

Не впевнений, чи це допоможе комусь іншому чи ні, але це було ключовою частиною для вирішення моєї проблеми у відповіді користувача "Тома Хоутіна - таклін" вище:

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

Якраз той факт, що "замок" передається як аргумент синхронізовано () і він також використовується в "lock" .notifyAll ();

Після того, як я зробив це в цих двох місцях, я працював


0

Я отримав IllegalMonitorStateExceptionчас, намагаючись прокинути нитку в / з іншої class/ нитки. У java 8ви можете використовувати lockфункції нового паралелізму API замість з synchronizedфункцій.

Я вже asynchronousзберігав об’єкти для трансакцій websocket в WeakHashMap. Рішення в моєму випадку було також зберегти lockоб'єкт вConcurrentHashMap протягом synchronousвідповідей. Приміткаcondition.await (НЕ .wait).

Для обробки декількох ниток я використовував a 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.