Как ждать, пока потоки ThreadPoolExecutor не завершатся? - PullRequest
0 голосов
/ 01 мая 2018

Примечание: это не попытка увидеть, была ли завершена задача, переданная в пул потоков, а завершение фактического потока.

Чтобы обнаружить утечки потоков, я хотел бы иметь код вида

val start = allThreads()
doStuff()
val end = allThreads()
assert start == end

Однако doStuff() может использовать один или несколько пулов потоков, которые на самом деле очищаются с помощью вызова shutdownNow() или аналогичного. Кажется, в ThreadPoolExecutor нет способа определить, все ли потоки были завершены. Такие вещи, как awaitTermination, только следят за тем, чтобы все потоки дошли до того, что они обязательно завершат работу, но не до того, что они уже сделали.

В идеале решение не является хакерским, например, использование отражения для прямого доступа к внутренним объектам класса (из которого, конечно, мы можем получить все потоки, а затем соединить их все).

ОБНОВЛЕНИЕ: причина для этого

Если у вас есть большая кодовая база, разработанная в течение некоторого времени, у вас могут быть проблемы с утечками потоков. Люди могут не закрывать исполнителей должным образом, люди могут просто вызывать new Thread(), люди могут вызывать сторонние библиотеки, которые запускают потоки, и либо код, либо код библиотеки не выходят из этих потоков. Ваши сборки потерпели неудачу из-за слишком большого количества потоков, запущенных в одном процессе, так как этот процесс может выполнить тысячи тестов, каждый из которых будет пропускать несколько потоков.

Чтобы предотвратить это, вы заставляете свои тесты проверять, чтобы потоки до теста и потоки после были одинаковыми. Это можно сделать, например, отражением, проверкой того, что каждый тестовый класс наследуется от базового класса, а затем в базовом классе, имеющем До / После для проверки потоков. Детали не важно, но в основном у нас есть детектор утечки нитей, который не проходит тест на утечки нитей.

Теперь, если вы правильно используете ThreadPoolExecutor и вызываете shutdownNow, мы не хотим, чтобы тест не прошел из-за детектора утечки. Однако простое использование shutdownNow может привести к ложным срабатываниям, потому что даже если мы успешно вызвали shutdownNow и вернулись из остальной части кода и находимся в фазе After, где мы проверяем текущие потоки, потоки пула потоков в это время могут быть еще живы. Поэтому мы хотим каким-то образом гарантировать, что потоки в пуле уже вышли до возврата, чтобы избежать ложных срабатываний.

1 Ответ

0 голосов
/ 01 мая 2018

Использовать фабрику нитей.

    ThreadFactory tf = runnable -> {
        return new Thread(() -> {
            try {
                runnable.run();
            } finally {
                System.out.println(Thread.currentThread().getName()+": my thread exit");
            }
        });
    };

    ExecutorService svc = Executors.newFixedThreadPool(3, tf);
    Future f = svc.submit(() -> System.out.println(Thread.currentThread().getName()+": some task"));
    f.get();
    svc.shutdown();

    Thread.sleep(2000);
    System.out.println("main done");
...