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.
Някаква идея какво пропускам?

Аз съм на build 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() само веднъж след {. Във всеки случай, ако се върне true, прекъсвам; и незабавно убийте нишката (и не докосвам/проверявам прекъсването никъде другаде). Ще разгледам UncaughtExceptionHandler. благодаря много за вашия отговор. (BTW имайте предвид този бъг в 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(). (BTW има грешка с това: stackoverflow.com/questions/2012259 ) Не се занимавам със състоянието на прекъсване в doSomething . - person marts; 21.01.2010

Потенциален проблем, че прекъсванията често се преглъщат. Така че някъде дълбоко в doSomething() (или дори в зареждането на клас), прекъсването може да бъде уловено от, да речем, wait() и след това отхвърлено от "небрежен" код. Прекъсванията са зло, IMO.

Може да си струва да проверите дали всички ваши задачи действително се изпълняват по време на анулирането.

person Tom Hawtin - tackline    schedule 21.01.2010