Pull to refresh

Java. Остановись задача

Reading time 2 min
Views 94K
Вот уже почти год как усиленно занимаюсь коддингом на Java. Столкнулся с довольно серьезной на мой взгляд проблемой, связанных с многопоточностью, как мне кажется, неразрешимой в рамках текущей реализации JVM от Oracle (сказанное относится к JDK 1.5 и выше). Дело в том, что на данный момент в Java нет возможности гарантированно безопасно остановить выполнение какого-либо потока. Данный пост разъясняет почему это именно так и предлагает начать дискуссию о способах решения этой проблемы.

Казалось бы тривиальная задача: имеем некий Thread (поток), который, мы точно знаем, безнадежно завис (может зациклился, может, что-то еще), при этом потребляет некоторые ресурсы. Что нам с ним делать? Хотелось бы ресурсы наши освободить. Казалось бы, что проще? Ан нет, при детальном изучении вопроса оказалось, что в JVM просто нет инструкции, чтобы корректно остановить зависший Thread. Старый метод Thread.stop() объявлен Deprecated и предан строжайшей анафеме. Как сказано в javadoc'е это метод «по сути небезопасен». Ну что же, если не безопасен не будем его использовать, дайте нам другой, безопасный метод. Но другого безопасного как ни странно не предлагается. Предлагается, безусловно, очень безопасная инструкция Thread.interrupt(). Но безопасна она, к сожалению, потому, что ровным счетом ничего не делает! Это всего лишь сообщение потоку: «Пожалуйста, остановись». Но если поток данное сообщение проигнорировал то… как сказано в документации «Если поток не отвечает на Thread.interrupt() вы можете использовать специфические для вашего приложению трюки». Спасибо, что разрешили. Что называется, крутись как хочешь.

Все становится еще сложней, если задача запущена в пуле потоков, через, например, ExecutorService.submit(Runnable). При этом мы даже не знаем, в каком именно потоке данная задача выполняется и уже не может применить даже запрещенный Thread.stop(). С другой стороны, мы имеем ссылку на Future, а у Future есть метод Future.cancel(boolean), который должен отменить выполнение задачи. Но если задача уже начала выполняться, вызов Future.cancel(true) на самом деле не остановит ее. В недрах реализации FutureTask выполняется код:

if (mayInterruptIfRunning) {
Thread r = runner;
if (r != null)
r.interrupt(); }

Т.е. опять потоку, в котором выполняется задача, всего лишь рекомендуется прекратить выполнение. К тому же, мы не имеем даже возможности узнать выполняется ли задача в данный момент или нет. Есть, вроде, метод Future.isDone(), но опять мимо, он возвращает true не только когда задача завершила выполнение, а сразу после вызова Future.cancel(), даже если задача все еще выполняется (ведь Future.cancel(true) не останавливает задачу которая уже начала выполняться).

Хорошо, если мы сами пишем весь код, тогда можно в нужных местах аккуратно обрабатывать Thread.isInterrupted() и все будет ОК. Но если мы запускаем сторонний код? Если у нас есть сервер расширяемый с помощью плагинов? Какой-нибудь криво написанный плагин может запросто привести к неработоспособному состоянию весь сервер ведь мы не можем корректно прервать выполнение зависшего плагина.

Признаюсь, я не знаю удовлетворительного решения. Может быть, метод Thread.stop() не так уж опасен? Очень хотелось бы услышать мнения Java программистов практиков по этому поводу.
Tags:
Hubs:
+24
Comments 29
Comments Comments 29

Articles