executorService.shutdownNow() не останавливает поток

У меня есть запланированная настройка службы Executor, как

class Test {
    private final ScheduledExecutorService executor;
    public Test() {
        executor = Executors.newSingleThreadScheduledExecutor((runnable) -> {
            Thread thread = new Thread(runnable, this.getClass().getName());
            thread.setDaemon(true);
            return thread;
        });
        executor.scheduleWithFixedDelay(
            new TestRefreshTask(), 0, 50000, TimeUnit.MILLISECONDS
        );
    }

    private class TestRefreshTask implements Runnable {
        @Override
        public void run() {
            //refresh some data
            refreshdata();
        }
    }

    public void close() {
        executor.shutdown();
        try {
            if(!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
                executor.awaitTermination(60, TimeUnit.SECONDS));
            }
        } catch (InterruptedException e) {
            //Retry to dispose task
            executor.shutdownNow();
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
    }
}

Чтобы остановить этот поток, я вызываю метод close, но часто поток не останавливается. Я не уверен, что не так, и если как-то я должен справиться с InterruptedException в моем коде run().

2 ответа

  • Реальное решение ниже линии. Эта часть описывает только подводные камни.

От Javadoc shutdownNow():

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

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

Тем не менее, ваша задача - это не то, что вы контролируете сами, это частная задача, созданная внутри scheduleWithFixedDelay(),

От Javadoc scheduleWithFixedDelay():

Если какое-либо выполнение задачи встречает исключение, последующие выполнения подавляются. В противном случае задание будет прекращено только путем отмены или прекращения исполнителя.

Это означает, что shutdownNow() не остановит поток. Часть отмены немного неясна для меня, так как Runnable в отличие от Future, не имеет cancel() метод. Другая причина, по которой задача будет остановлена, - это прекращение работы исполнителя, однако пул завершается только тогда, когда задачи останавливаются.


Реальное решение скрывается в этой части от Javadoc ScheduledThreadPoolExecutor:

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

Когда вы приказываете исполнителю остановиться, он устанавливает флаг прерывания, и если это ScheduledThreadPoolExecutor, удаляет задачу, если removeOnCancelPolicy правда.

Решение:

  • казнить executor.setRemoveOnCancelPolicy(true) перед отправкой графика.

[править]: Поскольку вы используете фабричный метод, это невозможно реализовать напрямую.

Сделайте эти изменения:

  • private final ScheduledThreadPoolExecutor executor;
  • executor = new ScheduledThreadPoolExecutor(1, (runnable) -> {

У меня была та же проблема, но в моем случае мне удалось прервать поток сам по себе. Когда основной поток выполняетexecutor.shutdownNow()он отправляет сигнал для каждого потока исполнителя, следовательно, каждый поток может идентифицировать свое состояние с помощьюThread.currentThread().isInterrupted().

Как я реализовал:

          if (Thread.currentThread().isInterrupted()) {
      return;
    }
Другие вопросы по тегам