Spec-Zone .ru
спецификации, руководства, описания, API
|
Thread.stop
осуждаемый?Поскольку это по сути опасно. Остановка потока заставляет это разблокировать все мониторы, которые это заблокировало. (Мониторы разблокированы как ThreadDeath
исключение распространяет стек.), Если какой-либо из объектов, ранее защищенных этими мониторами, был в непоследовательном состоянии, другие потоки могут теперь просмотреть эти объекты в непоследовательном состоянии. Такие объекты, как говорят, повреждаются. Когда потоки работают на поврежденных объектах, произвольное поведение может закончиться. Это поведение может быть тонким и трудным обнаружить, или это может быть объявлено. В отличие от других исключений непроверенных, ThreadDeath
уничтожает потоки тихо; таким образом у пользователя нет никакого предупреждения, что его программа может быть повреждена. Повреждение может проявиться в любое время после того, как фактическое повреждение происходит, даже часы или дни в будущем.
ThreadDeath
исключение и фиксирует поврежденный объект?В теории, возможно, но это значительно усложнило бы задачу записи корректного многопоточного кода. Задача была бы почти непреодолима по двум причинам:
ThreadDeath
исключение почти где угодно. Все синхронизируемые методы и блоки должны были бы быть изучены очень подробно с этим в памяти.ThreadDeath
исключение, очищая сначала (в catch
или finally
пункт). Уборка имела бы к повторному, пока она не успешно выполнялась. Код, чтобы гарантировать это был бы довольно сложен.Thread.stop(Throwable)
?В дополнение ко всем проблемам, отмеченным выше, этот метод может использоваться, чтобы генерировать исключения, которые его целевой поток неподготовлен, чтобы обработать (включая проверенные исключения, которые не мог возможно выдать поток, было это не для этого метода). Например, следующий метод поведенчески идентичен Java throw
работа, но обходит попытки компилятора гарантировать, что метод вызова объявил все проверенные исключения, которые это может выдать:
static void sneakyThrow(Throwable t) { Thread.currentThread().stop(t); }
Thread.stop
?Большинство использования stop
должен быть заменен кодом, который просто изменяет некоторую переменную, чтобы указать, что целевой поток должен прекратить работать. Целевой поток должен регулярно проверять эту переменную, и возврат из его метода выполнения аккуратным способом, если переменная указывает, что это должно прекратить работать. Чтобы гарантировать быструю передачу запроса остановки, переменной должен быть volatile (или доступ к переменной должен синхронизироваться).
Например, предположите, что Ваш апплет содержит следующий start
, stop
и run
методы:
private Thread blinker; public void start() { blinker = new Thread(this); blinker.start(); } public void stop() { blinker.stop(); // UNSAFE! } public void run() { while (true) { try { Thread.sleep(interval); } catch (InterruptedException e){ } repaint(); } }Можно избежать использования
Thread.stop
заменяя апплет stop
и run
методы с: private volatile Thread blinker; public void stop() { blinker = null; } public void run() { Thread thisThread = Thread.currentThread(); while (blinker == thisThread) { try { Thread.sleep(interval); } catch (InterruptedException e){ } repaint(); } }
Это что Thread.interrupt
метод для. То же самое "состояние базируемый" сигнальный механизм, показанный выше, может использоваться, но изменение состояния (blinker = null
, в предыдущем примере), может сопровождаться звонком Thread.interrupt
, прерывать ожидание:
public void stop() { Thread moribund = waiter; waiter = null; moribund.interrupt(); }Для этого метода, чтобы работать, является критическим, что любой метод, который ловит исключение прерывания и не готовится иметь дело с ним сразу, подтверждает исключение. Мы говорим, подтверждает, а не повторно бросает, потому что не всегда возможно повторно бросить исключение. Если метод, который ловит
InterruptedException
как объявляют, не выдает это (проверенное) исключение, тогда оно должно "повторно прервать себя" следующим колдовством: Thread.currentThread().interrupt();Это гарантирует, что Поток повторно повысит
InterruptedException
как только это в состоянии. Thread.interrupt
?В некоторых случаях можно использовать специализированные приемы. Например, если поток ожидает на известном сокете, можно закрыть сокет, чтобы заставить поток сразу возвратиться. К сожалению, действительно нет никакого метода, который работает вообще. Нужно отметить, что во всех ситуациях, где поток ожидания не отвечает на Thread.interrupt
, это не ответило бы на Thread.stop
также. Такие случаи включают преднамеренные атаки "отказ в обслуживании", и операции ввода-вывода, на которые thread.stop и thread.interrupt не работают должным образом.
Thread.suspend
и Thread.resume
осуждаемый?Thread.suspend
является по сути склонным к мертвой блокировке. Если целевой поток содержит блокировку на мониторе, защищающем критический системный ресурс, когда это приостанавливается, никакой поток не может получить доступ к этому ресурсу, пока целевой поток не возобновляется. Если поток, который возобновил бы целевой поток, пытается заблокировать этот монитор до вызова resume
, результаты мертвой блокировки. Такие мертвые блокировки обычно проявляются как "замороженные" процессы.
Thread.suspend
и Thread.resume
?Как с Thread.stop
, у благоразумного подхода должен быть "целевой поток", опрашивают переменную, указывающую на требуемое состояние потока (активный или приостановленный). Когда требуемое состояние приостанавливается, поток ожидает, используя Object.wait
. Когда поток возобновляется, целевой поток уведомляется, используя Object.notify
.
Например, предположите, что Ваш апплет содержит следующий mousePressed обработчик событий, который переключает состояние вызванного потока blinker
:
private boolean threadSuspended; Public void mousePressed(MouseEvent e) { e.consume(); if (threadSuspended) blinker.resume(); else blinker.suspend(); // DEADLOCK-PRONE! threadSuspended = !threadSuspended; }Можно избежать использования
Thread.suspend
и Thread.resume
заменяя обработчик событий выше: public synchronized void mousePressed(MouseEvent e) { e.consume(); threadSuspended = !threadSuspended; if (!threadSuspended) notify(); }и добавление следующего кода к "выполненному циклу":
synchronized(this) { while (threadSuspended) wait(); }
wait
метод бросает InterruptedException
, таким образом, это должно быть внутри a try ... catch
пункт. Прекрасно поместить это в тот же самый пункт как sleep
. Проверка должна следовать (а не предшествовать), sleep
таким образом, окно было сразу перекрашено, когда поток "возобновляется". Получающееся run
метод следует: public void run() { while (true) { try { Thread.sleep(interval); synchronized(this) { while (threadSuspended) wait(); } } catch (InterruptedException e){ } repaint(); } }Отметьте что
notify
в mousePressed
метод и wait
в run
метод внутри synchronized
блоки. Это требуется языком, и гарантирует это wait
и notify
должным образом сериализируются. На практике это устраняет условия гонки, которые могли заставить "приостановленный" поток пропускать a notify
и останьтесь временно отстраненными неопределенно. В то время как стоимость синхронизации в Java уменьшается, поскольку платформа назревает, это никогда не будет свободно. Простой прием может использоваться, чтобы удалить синхронизацию, которую мы добавили к каждой итерации "выполненного цикла." Синхронизируемый блок, который был добавлен, заменяется немного более сложной частью кода, который вводит синхронизируемый блок, только если поток был фактически приостановлен:
if (threadSuspended) { synchronized(this) { while (threadSuspended) wait(); } }
В отсутствие явной синхронизации threadSuspended должен быть сделан volatile, чтобы гарантировать быструю передачу приостанавливать-запроса.
Получающеесяrun
метод: private volatile boolean threadSuspended; public void run() { while (true) { try { Thread.sleep(interval); if (threadSuspended) { synchronized(this) { while (threadSuspended) wait(); } } } catch (InterruptedException e){ } repaint(); } }
Чтобы исправить эту ситуацию, метод stop должен гарантировать, что целевой поток сразу возобновляется, если это приостанавливается. Как только целевой поток возобновляется, он должен сразу распознать, что был остановлен, и выходит корректно. Вот то, как получающиеся методы run И stop смотрят:
public void run() { Thread thisThread = Thread.currentThread(); while (blinker == thisThread) { try { Thread.sleep(interval); synchronized(this) { while (threadSuspended && blinker==thisThread) wait(); } } catch (InterruptedException e){ } repaint(); } } public synchronized void stop() { blinker = null; notify(); }Если вызовы метода stop Thread.interrupt, как описано выше, это не должно вызвать notify также, но это все еще должно синхронизироваться. Это гарантирует, что целевой поток не будет пропускать прерывание из-за условия гонки.
Thread.destroy
?Thread.destroy
никогда не реализовывался и был осужден. Если бы это было реализовано, то это было бы склонным к мертвой блокировке таким образом Thread.suspend
. (Фактически, это примерно эквивалентно Thread.suspend
без возможности последующего Thread.resume
.) Runtime.runFinalizersOnExit
осуждаемый?Далее, вызов не "ориентирован на многопотоковое исполнение" в том смысле, что он устанавливает VM-глобальный флаг. Это вынуждает каждый класс с финализатором защитить против завершения живых объектов!