Вопрос

Товарищ по команде сделал следующее утверждение:

"Thread.interrupt() по своей сути сломан и не должен (почти) никогда не использоваться ».

Я пытаюсь понять, почему это так.

Это известная лучшая практика никогда не использовать Thread.interrupt()? Можете ли вы предоставить доказательства, почему он сломан / багги, и не следует использовать для написания надежного многопоточного кода?

Примечание - Меня не интересует этот вопрос, если он «симпатичный» от консерванта дизайна. У меня вопрос - это глюка?

Это было полезно?

Решение

Укороченная версия:

Это известная лучшая практика никогда не использовать Thread.interrupt ()?

Нет.

Можете ли вы предоставить доказательства, почему он сломан / багги, и не следует использовать для написания надежного многопоточного кода?

Напротив верно: это важно для многопоточного кода.

См. Листинг 7.7 в Ява параллелизм на практике Для примера.

Более длинная версия:

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

try {
    // Some code that might throw an InterruptedException.  
    // Using sleep as an example
    Thread.sleep(10000);
} catch (InterruptedException ie) {
    System.err.println("Interrupted in our long run.  Stopping.");
    Thread.currentThread().interrupt();
}

Это делает для нас две вещи:

  1. Это избегает употребления исключения прерывания. Обработчики ide Aut-Exception всегда предоставляют вам что-то вроде ie.printStackTrace(); И веселый "Тодо: что -то полезное нужно сюда!" комментарий.
  2. Он восстанавливает статус прерывания, не принуждая проверенное исключение в этом методе. Если подпись метода, которую вы реализуете, не имеет throws InterruptedException Пункт, это ваш другой вариант для распространения этого прерванного статуса.

Комментатор предложил мне использовать неконтролируемое исключение, «чтобы заставить поток умереть». Это предполагает, что у меня есть предварительные знания, что убить нить резко - это правильная вещь. Я не.

Процитировать Брайана Гетца из JCIP на странице перед указанным выше списками:

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

Например, представьте, что я сделал это:

} catch (InterruptedException ie) {
    System.err.println("Interrupted in our long run.  Stopping.");
    // The following is very rude.
    throw new RuntimeException("I think the thread should die immediately", ie);
}

Я бы заявил, что, независимо от других обязательств остальной части стека вызовов и связанного состояния, этот поток должен умереть прямо сейчас. Я бы попытался пройти мимо всех остальных блоков улова и государственного кода очистки, чтобы сразу же добраться до поток. Хуже того, я бы поглотил прерыванный статус потока. Вверх по течению логика теперь должна была бы деконструировать мое исключение, чтобы попытаться заглушить, была ли ошибка логики программы или я пытаюсь скрыть проверенное исключение внутри скрытной обертки.

Например, вот что все остальные в команде должны сразу же сделать:

try {
    callBobsCode();
} catch (RuntimeException e) { // Because Bob is a jerk
    if (e.getCause() instanceOf InterruptedException) {
        // Man, what is that guy's problem?
        interruptCleanlyAndPreserveState();
        // Restoring the interrupt status
        Thread.currentThread().interrupt();
    }
}

Прерванное состояние важнее любого конкретного InterruptException. Анкет Для конкретного примера, почему см. Javadoc для Thread.interrupt ():

Если этот поток заблокирован при вызове wait (), подождите (долго) или подождите (длинные, инт) методы класса объекта или join (), join (long), join (long, int) , Сон (длинный) или сон (длинный, int), методы этого класса, затем его состояние прерывания будет очищено, и он получит прерывание.

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

Другие советы

Единственный способ, которым я знаю, в котором Thread.interrupt() сломан, что на самом деле он не делает то, что может, - это может только на самом деле прерывать Код, который слушает это.

Тем не менее, используется правильно, мне кажется хорошим встроенным механизмом для управления задачами и отмены.

Я рекомендую Ява параллелизм на практике Для получения дополнительной информации о правильном и безопасном использовании этого.

Главная проблема с Thread.interrupt() Это то, что большинство программистов не знают о скрытых ловушках и не используют их неправильно. Например, когда вы обрабатываете прерывание, есть методы, которые Чисто Флаг (так что статус теряется).

Кроме того, звонок не всегда будет сразу прерывать поток. Например, когда он висит в какой -то системной рутине, ничего не произойдет. На самом деле, если поток не проверяет флаг и никогда не называет метод Java, который бросает InterruptException, тогда прерывание этого не будет иметь никакого эффекта.

Нет, это не багги. На самом деле это основа того, как вы останавливаете темы в Java. Он используется в рамках исполнителя от java.util.concurrent - см. Реализацию java.util.concurrent.FutureTask.Sync.innerCancel.

Что касается неудачи, я никогда не видел, чтобы это не удалось, и я широко его использовал.

Одна из причин, не упомянутой, заключается в том, что сигнал прерывания может быть потерян, что делает вызов методу потока. Enterrupt () бессмысленным. Таким образом, если ваш код в методе Thread.Run () не вращается во время цикла результата вызова потока. Interrupt () не является неопределенным.

Я заметил это, когда в потоке ONE Я выполняю DriverManager.getConnection() Когда нет доступного подключения к базе данных (скажем, сервер падает, поэтому эта строка бросает SQLException) и из потока TWO Я явно звоню ONE.interrupt(), тогда оба ONE.interrupted() а также ONE.isInterrupted() возвращаться false Даже если помещен как первая строка в catch{} Блок где SQLException обрабатывается.

Конечно, я использовал эту проблему, внедряя дополнительный семафор, но это довольно хлопотно, так как это самая первая такая проблема в моей 15 -летней разработке Java.

Я полагаю, это из -за ошибки в com.microsoft.sqlserver.jdbc.SQLServerDriver. Анкет И я исследовал больше, чтобы подтвердить, что призыв к native Функция потребляет это исключение во всех случаях, что он делает свое собственное, но сохраняет его, когда он преуспел.

Томек

Ps я нашел аналогичная проблема.

PPS Я заключаю очень короткий пример того, что я пишу выше. Зарегистрированный класс можно найти в sqljdbc42.jar. Анкет Я нашел эту ошибку в классах, построенных на 2015-08-20, затем я обновился до новой доступной версии (с 2017-01-12), и ошибка все еще существует.

import java.sql.*;

public class TEST implements Runnable{
    static{
        try{
//register the proper driver
           Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
        }
        catch(ClassNotFoundException e){
            System.err.println("Cannot load JDBC driver (not found in CLASSPATH).");
        }
    }

    public void run() {
        Thread.currentThread().interrupt();
        System.out.println(Thread.currentThread().isInterrupted());
//prints true
        try{
            Connection conn = DriverManager.getConnection("jdbc:sqlserver://xxxx\\xxxx;databaseName=xxxx;integratedSecurity=true");
        }
        catch (SQLException e){
            System.out.println(e.getMessage());
        }
        System.out.println(Thread.currentThread().isInterrupted());
//prints false
        System.exit(0);
    }

    public static void main(String[] args){
        (new Thread(new TEST())).start();
    }
}

Если вы передаете что -то совершенно неверное, как "foo", в DriverManager.getConnection(), Вы получите сообщение «Не найдено подходящего драйвера для Foo», и вторая распечатка будет все еще верна, как и следовало ожидать. Но если вы передаете правильно построенную строку, но, скажем, ваш сервер падает или вы потеряли сетевое соединение (которое, как правило, возникает в производственной среде), вы увидите java.net Печать ошибки тайм -аута interrupted() Государство потеряно.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top