ExecutorService workStealingPool и метод отмены

Можете ли вы представить себе причину, по которой этот код не работает и всегда выводит «готово», но второй пример работает без проблем. Я использую последнюю версию 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, но вы этого не сделали; вы позволяете логике метода продолжаться после блока перехвата. Таким образом, даже когда поток прерывается, всегда выполняется последний оператор run метода.

person VGR    schedule 27.04.2015
comment
Виноват. Я быстро набросал этот пример из более сложного кода и забыл добавить return. Основная идея моего вопроса немного другая - см. Edit. - person JiriS; 28.04.2015