Spec-Zone .ru
спецификации, руководства, описания, API
Содержание документации

Поток Java Примитивное Осуждение


Почему Thread.stop осуждаемый?

Поскольку это по сути опасно. Остановка потока заставляет это разблокировать все мониторы, которые это заблокировало. (Мониторы разблокированы как ThreadDeath исключение распространяет стек.), Если какой-либо из объектов, ранее защищенных этими мониторами, был в непоследовательном состоянии, другие потоки могут теперь просмотреть эти объекты в непоследовательном состоянии. Такие объекты, как говорят, повреждаются. Когда потоки работают на поврежденных объектах, произвольное поведение может закончиться. Это поведение может быть тонким и трудным обнаружить, или это может быть объявлено. В отличие от других исключений непроверенных, ThreadDeath уничтожает потоки тихо; таким образом у пользователя нет никакого предупреждения, что его программа может быть повреждена. Повреждение может проявиться в любое время после того, как фактическое повреждение происходит, даже часы или дни в будущем.


Не мог я только поймать ThreadDeath исключение и фиксирует поврежденный объект?

В теории, возможно, но это значительно усложнило бы задачу записи корректного многопоточного кода. Задача была бы почти непреодолима по двум причинам:

  1. Поток может бросить a ThreadDeath исключение почти где угодно. Все синхронизируемые методы и блоки должны были бы быть изучены очень подробно с этим в памяти.
  2. Поток может бросить секунду 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 просто установит переменную состояния (blinker) в нуль, то целевой поток останется приостановленным (ожидающий на мониторе), вместо того, чтобы выйти корректно, как это должно. Если апплет перезапускается, многократные потоки могли бы закончить тем, что ожидали на мониторе одновременно, приводя к ошибочному поведению.

Чтобы исправить эту ситуацию, метод 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-глобальный флаг. Это вынуждает каждый класс с финализатором защитить против завершения живых объектов!


Oracle и/или его филиалы Авторское право © 1993, 2011, Oracle и/или его филиалы. Все права защищены.
Свяжитесь с Нами