#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. Или просто используйте параллельные классы, которые уже выполняют большую часть подобных действий.