ExecutorService работи StealingPool и метод за отмяна

Можете ли да помислите за някаква причина, поради която този код не работи и винаги извежда "finished", но вторият пример работи без никакви проблеми. Използвам най-новия JDK (8u45).

public static class MyClass implements Runnable {

        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException ex) {
                System.out.println("Interrupted");
                return;
            }
            System.out.println("Finished");
        }

        public static void main(String[] args) {
             // spot the difference ->
             ExecutorService executorService = Executors.newWorkStealingPool(4);
             Future future = executorService.submit(new MyClass());
             Thread.sleep(100);
             future.cancel(true);
        }
    }

И следният пример работи безупречно:

public static class MyClass implements Runnable {

        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException ex) {
                System.out.println("Interrupted");
                return;
            }
            System.out.println("Finished");
        }

        public static void main(String[] args) {
             ExecutorService executorService = Executors.newSingleThreadExecutor();
             Future future = executorService.submit(new MyClass());
             Thread.sleep(100);
             future.cancel(true);
        }
    }

РЕДАКТИРАНЕ: Добавено връщане и актуализирано време за заспиване и друг пример.


person JiriS    schedule 27.04.2015    source източник
comment
Уау, това е притеснително. В моята система (Java 1.8.0_45-b14, Linux 3.2.0-4-amd64) не отпечатва нищо. Точно като вас, установявам, че само ExecutorService се връща от newWorkStealingPool има този проблем. Един от нас трябва да изпрати това като грешка.   -  person VGR    schedule 28.04.2015


Отговори (2)


По-просто е, отколкото си мислех първоначално. Проблемът е, че work-stealing-pool използва вътрешно ForkJoinPool и ForkJoinTask не поддържа cancel(true) и следователно не е възможно да отмените задачата, след като задачата е стартирана.

Вижте документацията на javadoc (http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinTask.html):

   mayInterruptIfRunning - this value has no effect in the default implementation 
   because interrupts are not used to control cancellation.
person JiriS    schedule 28.04.2015
comment
Колко неудобно; Просто не чаках съня (2000) да свърши. Както и да е, добра находка. Това е полезно да знаете. - person VGR; 28.04.2015
comment
В ретроспекция това е по-очевидно, тъй като изпълнителят на една нишка би означавал, че няма нужда да се разклонява друга нишка. Ще ми е любопитно какво ще се случи, ако използвате .newFixedThreadPool(1) - person djangofan; 31.07.2017

Няма начин за принудително прекратяване на нишка в Java. (Преди двадесет години Java 1.0 се опита да предостави това и се оказа неработоспособно; методите, които се опитаха да го направят, са отхвърлени без замяна.)

Вие, като автор на Runnable, сте отговорни за правилното реагиране на прекъсване чрез чисто прекратяване на вашия собствен run метод. Във вашия случай трябваше да излезете от вашия run метод в catch-блока, но не сте го направили; оставяте логиката на метода да продължи след catch-блока. Така че дори когато нишката е прекъсната, последното изявление на метода run винаги се изпълнява.

person VGR    schedule 27.04.2015
comment
Моя грешка. Начертах този пример набързо като извлечение от малко по-сложен код и забравих да добавя return. Основната идея на моя въпрос е малко по-различна - вижте редактиране. - person JiriS; 28.04.2015