Pull to refresh

Comments 12

С появлением виртуальных потоков я бы добавил, что для IO-bound задач в принципе больше нет необходимости в пуле потоков, будет достаточно Executors.newVitrualThreadPerTaskExecutor()

Валидно, но я не придумал как подружить их с TaskDecorator (так как контекст логирования ThreadLocal и не передается между потоками по умолчанию)

Можно попробовать использовать InheritableThreadLocal

Рассмотрю это решение

Еще бы увидеть код с этой ситуацией.

// Плохая конфигурация (причина deadlock)
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1); // Всего ОДИН поток!
executor.initialize();

// Главный поток
CompletableFuture<?> task1 = CompletableFuture.supplyAsync(() -> {
    // Внутри задачи 1...
    CompletableFuture<?> subTask1 = CompletableFuture.supplyAsync(..., executor);
    CompletableFuture<?> subTask2 = CompletableFuture.supplyAsync(..., executor);
    
    // БЛОКИРОВКА! Используем allOf().join() для ожидания всех подзадач
    return CompletableFuture.allOf(subTask1, subTask2).join(); 
}, executor);

CompletableFuture<?> task2 = CompletableFuture.supplyAsync(..., executor);
CompletableFuture.allOf(task1, task2).join(); // Вечное ожидание...

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

Валидно, но пул потоков в любом случае нужно было расширять

Вместо решения проблемы, её замели под ковер.

В конечном итоге пришли к тому, чтобы не блокировать потоки в дочерних задачах

Можно использовать эластичный пул
public ExecutorService myExecutorService() {

BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1000); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1000, 1000, 60L, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.AbortPolicy()); threadPoolExecutor.allowCoreThreadTimeOut(true);

return threadPoolExecutor;

}

«эластичный» пул с большим числом потоков и очередью не устраняет корневую причину — блокировку рабочего потока из‑за join внутри того же пула, где должны стартовать дочерние задачи, поэтому риск starvation/дедлока остаётся и лишь «маскируется» масштабированием. Решение — не блокировать воркеры: компоновать этапы через thenCompose/thenCombine/allOf и делать единственный join на границе, либо выносить дочерние задачи в отдельный исполнитель или виртуальные потоки для изоляции от блокировок.

Sign up to leave a comment.

Articles