Java Thread - странное поведение Thread.interrupted() и future.cancel(true)

Я хочу управлять списком объектов Futures, возвращаемых моим TaskExecutor.
У меня есть что-то вроде этого

 List<Future<String>> list 

 void process(ProcessThis processThis) {     
    for ( ...) {
       Future<String> future = taskExecutor.submit(processThis);
       list.add(future)
    }
 }


 void removeFutures() {
    for(Future future : list) {
       assert future.cancel(true);
 }

ProcessThis — это задача, реализующая Callable‹ String> и проверяющая статус Thread.interrupted().

    public String call() {
        while (true) {
            if (Thread.interrupted()) {
                break;
            }
            doSomething();
        }
    }

Теперь проблема в том, что только подмножество параллельных потоков возвращает «истину» при вызове Thread.interrupted().
Утверждение в removeFutures() возвращает true для каждого удаляемого будущего (я также проверял isDone() и isCompleted()).
Количество прерванных потоков является случайным. Более 15 запущенных потоков иногда 13 прервано, иногда 2 ...
Я действительно не понимаю, в чем проблема, если я вызываю future.cancel(true) и это возвращает true... и затем я проверяю Thread.interrupted (это вызывается только один раз) , я ожидаю, что это также вернет true.
Есть идеи, что я упустил?

Я использую сборку java 1.6.0_02-b05.


person marts    schedule 21.01.2010    source источник


Ответы (3)


Имейте в виду, что Thread.interrupted() возвращает текущий статус прерывания, а затем очищает его, поэтому все будущие вызовы будут возвращать false. То, что вы хотите, вероятно, Thread.currentThread().isInterrupted().

Также имейте в виду, что future.cancel(true) обычно возвращает false только в том случае, если задача уже была завершена или отменена. Если он возвращает true, это не гарантирует, что задача действительно будет отменена.

Что происходит в doSomething()? Возможно, из-за прерывания куда-то убегает RuntimeException. У вас есть набор UncaughtExceptionHandler? Если нет, вам необходимо передать ThreadFactory на Executor, который установит обработчик исключений и зарегистрирует все пропущенные исключения.

person Kevin    schedule 21.01.2010
comment
Я знаю об этом. Я вызываю Thread.interrupted() только один раз после while {. В любом случае, если он вернет true, я сломаюсь; и немедленно убить поток (и я больше нигде не трогаю / не проверяю прерывание). Я посмотрю на UncaughtExceptionHandler. большое спасибо за ваш ответ. (Кстати, имейте в виду эту ошибку в Thread.currentThread().isInterrupted() bugs.sun .com/view_bug.do?bug_id=6772683) - person marts; 21.01.2010
comment
В настоящее время вы правильно используете Thread.interrupted() безопасным образом. Однако, если вам не нужно поведение, обеспечиваемое этим методом, вы не должны использовать его, поскольку кто-то другой (или вы сами) может реорганизовать этот метод таким образом, что Thread.interrupted() вызывается таким образом, что делает недействительной обработку прерывания. . Гораздо безопаснее использовать Thread.currentThread().isInterrupted(), если можете. - person Kevin; 21.01.2010
comment
Привет, Кевин. Вы правы, но из-за проблемы с лицензией Java я не могу обновить текущую JVM 1.6.0_02-b05. Я работаю на многопроцессорной машине, и на мою JVM может повлиять ошибка, о которой я упоминал в предыдущем комментарии (вот связанный пост в stackoverflow stackoverflow.com/questions/2012259). Что касается необработанного исключения, если RuntimeException побег, я могу представить, что поток умрет сам по себе, верно? вот моя проблема в обратном.. они не останавливаются. (или, по крайней мере, часть из них остановлена) - person marts; 21.01.2010
comment
1.) Какая проблема с лицензией Java? 2.) Что делает doSomething()? Это правильно реагирует на прерывание? - person Kevin; 21.01.2010

По крайней мере, вы должны восстановить флаг прерывания, чтобы taskExecutor знал о прерывании потока:

public String call() { 
    while (true) { 
        if (Thread.interrupted()) { 
            Thread.currentThread().interrupt();
            break; 
        } 
        doSomething(); 
    } 
} 
person axtavt    schedule 21.01.2010
comment
Спасибо за ваш ответ. какой смысл это делать? Если Thread.interrupted() возвращает true, я прерываю цикл while и фактически убиваю поток. Проблема в том, что иногда Thread.interrupted() возвращает «false», даже если связанный с ним future.cancel(true) возвращает true. Строка, которую вы отредактировали, в это время даже не может быть достигнута. - person marts; 21.01.2010
comment
Тогда, вероятно, где-то в doSomething() потерян флаг прерывания (по той же причине - что-то сбрасывает флаг и не восстанавливает в). То есть образец в моем ответе — это базовый принцип, который следует использовать, чтобы избежать потерянных прерываний. - person axtavt; 21.01.2010
comment
Если это так («избегайте потерянных прерываний»), не мог бы я просто использовать что-то вроде Thread.currentThread().isInterrupted() вместо того, чтобы восстанавливать состояние прерывания каждый раз, когда я использую Thread.interrupted(). (Кстати, с этим есть ошибка: stackoverflow.com/questions/2012259). Я не имею дело со статусом прерывания в doSomething . - person marts; 21.01.2010

Потенциальная проблема в том, что прерывания часто проглатываются. Таким образом, где-то глубоко в doSomething() (или даже при загрузке класса) прерывание может быть перехвачено, скажем, wait(), а затем отброшено «небрежным» кодом. Прерывания - зло, ИМО.

Возможно, стоит проверить, что все ваши задачи действительно выполняются во время отмены.

person Tom Hawtin - tackline    schedule 21.01.2010