Исключение IllegalMonitorStateException, вызывающее wait() в потоке внутри его метода run (без синхронизированного блока)

#java #multithreading #exception

#java #многопоточность #исключение

Вопрос:

Я приказываю убедиться, что поток, считывающий данные из сокета, останавливается при закрытии сокета (из-за того socket.isClosed() , что он работает не так, как ожидалось) Я написал «сердцебиение», чтобы проверить, открыт ли сокет. Метод startHeartbeat() вызывается непосредственно перед тем, как BufferedReader начнет чтение сокета, и он начинает чтение только тогда, когда isClosed() значение равно false .

Здесь задействованы некоторые синхронизированные методы, но, в отличие от других подобных вопросов, wait() вызов не входит ни в один из методов. Вот основной код:

 synchronized boolean isClosed()
{
    return closed;
}

synchronized void setClosed(boolean b)
{
    closed = b;
}

//We need to make sure that the socket is still online, to ensure the reading stops when the connection closes.
void startHeartbeat()
{
    Thread heartbeat = new Thread()
    {
        public void run()
        {
            while (true)
            {
                try
                {
                    post(THUMP_THUMP);
                    setClosed(false);
                }
                catch (IOException e)
                {
                    setClosed(true);
                }
                finally
                {
                    try
                    {
                        this.wait(PULSE); //Exception here!
                    }
                    catch (InterruptedException e) {}
                }
            }
        }
    };
    heartbeat.setDaemon(true);
    heartbeat.start();
}
  

THUMP_THUMP — это просто постоянная строка, которая отправляется ( post() метод просто записывает объект в a BufferedWriter ), а PULSE — это время между ударами.

Я не уверен, почему существует IllegalMonitorStateException here, после прочтения различных потоков (я понимаю, почему у них есть исключение) и чтения API для исключения. Кто-нибудь может сказать мне, что я здесь делаю не так?

Комментарии:

1. Что говорит API?

2. @SotiriosDelimanolis В нем говорится, что исключение срабатывает, когда потоку не принадлежит объект, для которого он вызывает wait . Насколько я знаю, этот поток владеет объектом, поскольку объект сам по себе…

3. Он не говорит it doesn't own the object . В нем говорится it doesn't own the object's monitor . Что это за монитор?

4. Ну, я думаю, это будет основной поток или поток, который тогда вызвал метод…

5. @J_mie6: С чего бы это? Я думаю, вы неправильно поняли, что значит владеть монитором объекта: это все synchronized .

Ответ №1:

Кто-нибудь может сказать мне, что я здесь делаю не так?

Да — вы не синхронизируете ожидаемый объект. Документация по API довольно понятна:

Выдает:
IllegalMonitorStateException — если текущий поток не является владельцем монитора объекта.

Важно понимать, что «владелец монитора объектов» в основном означает «находится в синхронизированном блоке, использующем этот объект». Синхронизированный блок получает монитор соответствующего объекта в начале и освобождает его в конце. Из руководства по Java по синхронизации:

Синхронизация строится вокруг внутренней сущности, известной как внутренняя блокировка или блокировка монитора. (Спецификация API часто ссылается на эту сущность просто как «монитор».) Встроенные блокировки играют роль в обоих аспектах синхронизации: обеспечение эксклюзивного доступа к состоянию объекта и установление отношений «происходит до», которые необходимы для видимости.

С каждым объектом связана внутренняя блокировка. По соглашению, поток, которому требуется эксклюзивный и согласованный доступ к полям объекта, должен получить внутреннюю блокировку объекта перед доступом к ним, а затем снять внутреннюю блокировку, когда это будет сделано с ними. Говорят, что потоку принадлежит внутренняя блокировка между моментом, когда он получил блокировку и снял блокировку. Пока поток владеет встроенной блокировкой, никакой другой поток не может получить такую же блокировку. Другой поток будет заблокирован при попытке получить блокировку.

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

Другой способ создания синхронизированного кода — использовать синхронизированные операторы. В отличие от синхронизированных методов, операторы synchronized должны указывать объект, который обеспечивает внутреннюю блокировку […]

В вашем коде текущий поток не является владельцем this , потому что вы не синхронизируете — отсюда и исключение. Вы должны синхронизировать — помните, что wait() это освободит монитор, а затем повторно получит его перед возвратом.

В качестве примечания я бы настоятельно рекомендовал не использовать Thread монитор объекта для синхронизации, ожидания, уведомления и т. Д. — Код внутри Thread уже делает это, Поэтому ваш код и его код могут легко мешать друг другу. Я бы рекомендовал создать отдельный объект исключительно с целью синхронизации / ожидания / уведомления.

Комментарии:

1. хорошо, теперь я немного лучше понимаю … если вы вызываете wait() вне самого потока (т. Е. В другом потоке), почему нет необходимости иметь его в синхронизированном блоке?

2. @J_mie6: Вам всегда нужен synchronized где-то блок, если вы собираетесь звонить wait , иначе он выдаст исключение. Блок может быть выше по стеку — важно то, что поток должен владеть монитором.

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

4. Или просто используйте параллельные классы, которые уже выполняют большую часть подобных действий.