Как закрыть службу-исполнитель?

Всякий раз, когда я вызываю shutdownNow() или shutdown(), он не закрывается. Я читал несколько тем, где говорилось, что закрытие не гарантировано - может ли кто-нибудь предоставить мне хороший способ сделать это?

Ответ 1

Типичный шаблон:

executorService.shutdownNow();
executorService.awaitTermination();

При вызове shutdownNow исполнитель (обычно) пытается прервать потоки, которыми он управляет. Чтобы сделать shutdown изящным, вам нужно поймать прерванное исключение в потоках или проверить прерванный статус. Если ваши потоки не будут выполняться вечно, и ваш исполнитель никогда не сможет завершить работу. Это связано с тем, что прерывание потоков в Java является совместным процессом (т.е. прерванный код должен что-то делать, когда его просят остановить, а не прерывания).

Например, следующий код печатает Exiting normally.... Но если вы закомментируете строку if (Thread.currentThread().isInterrupted()) break;, она будет печатать Still waiting..., потому что потоки внутри исполнителя все еще запущены.

public static void main(String args[]) throws InterruptedException {
    ExecutorService executor = Executors.newFixedThreadPool(1);
    executor.submit(new Runnable() {

        @Override
        public void run() {
            while (true) {
                if (Thread.currentThread().isInterrupted()) break;
            }
        }
    });

    executor.shutdownNow();
    if (!executor.awaitTermination(100, TimeUnit.MICROSECONDS)) {
        System.out.println("Still waiting...");
        System.exit(0);
    }
    System.out.println("Exiting normally...");
}

В качестве альтернативы он может быть записан с помощью InterruptedException следующим образом:

public static void main(String args[]) throws InterruptedException {
    ExecutorService executor = Executors.newFixedThreadPool(1);
    executor.submit(new Runnable() {

        @Override
        public void run() {
            try {
                while (true) {Thread.sleep(10);}
            } catch (InterruptedException e) {
                //ok let get out of here
            }
        }
    });

    executor.shutdownNow();
    if (!executor.awaitTermination(100, TimeUnit.MICROSECONDS)) {
        System.out.println("Still waiting...");
        System.exit(0);
    }
    System.out.println("Exiting normally...");
}

Ответ 2

Лучший способ - это то, что мы имеем в javadoc, которое:

Следующий метод отключает ExecutorService в две фазы, сначала вызовите shutdown, чтобы отклонить входящие задачи, а затем вызовите shutdownNow, если необходимо, для отмены любых затяжных задач:

void shutdownAndAwaitTermination(ExecutorService pool) {
    pool.shutdown(); // Disable new tasks from being submitted
    try {
        // Wait a while for existing tasks to terminate
        if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
            pool.shutdownNow(); // Cancel currently executing tasks
            // Wait a while for tasks to respond to being cancelled
            if (!pool.awaitTermination(60, TimeUnit.SECONDS))
                System.err.println("Pool did not terminate");
        }
    } catch (InterruptedException ie) {
        // (Re-)Cancel if current thread also interrupted
        pool.shutdownNow();
        // Preserve interrupt status
        Thread.currentThread().interrupt();
    }
}