3.4Kпросмотров
48.3%от подписчиков
20 марта 2026 г.
Score: 3.7K
⌨️ Принёс вам одну из задач с собесов: "Что выведется в каких потоках и почему?" var executor = Executors.newFixedThreadPool(...); CompletableFuture.supplyAsync(() -> { log("1"); ... }, executor ).thenApply(v -> { log("2"); ... } ).thenCombine(otherFuture, (a, b) -> { log("3"); ... } ).thenApplyAsync(v -> { log("4"); ... } ).whenComplete((r, e) -> { log("5"); }
); Напишите для каждого лога в каком потоке может выполниться и почему именно там Неочевидный момент: здесь недостаточно сказать "Нуууу, всё пойдёт в executor". Потому что не пойдёт, и на этом моменте человек поплыл Разбор: Лог 1 suplyAsync(..., executor) выполняется в одном из потоков переданного executor Лог 2 thenApply(...) - это не async-версия. Она выполняется в потоке, который завершил предыдущий stage. Значит лог 2 обычно будет в том же потоке executor, который выполнил лог 1 Лог 3 thenCombine(...) - тоже не async. НО тут уже более тонкий момент - здесь зависимость уже от двух future: текущей и otherFuture Колбэк выполнится в потоке, который последним завершит одну из этих двух зависимостей. То есть лог 3 может оказаться: ИЛИ в потоке executor, если последней завершилась левая ветка, ИЛИ в потоке, который завершил otherFuture Лог 4 thenApplyAsync(...) - уже async-версия Если executor явно не передан, то выполнение уйдёт в ForkJoinPool.commonPool() Значит лог 4 обычно будет уже не в изначальном executor, а в commonPool Лог 5 whenComplete(...) - снова не async. Значит он выполнится в потоке, который завершил предыдущий stage То есть в том же потоке ForkJoinPool.commonPool, где отработал лог 4 Если отвечать кратко: 1 - поток из переданного executor
2 - тот же поток из executor
3 - поток, завершивший вторую из двух зависимостей в thenCombine
4 - поток из ForkJoinPool.commonPool
5 - тот же поток, что из лога 4 Правило, которое вынести из этой задачи и запомнить: 1. xxxAsync(...) => отдельная асинхронная постановка задачи, часто в ForkJoinPool.commonPool, если executor не передан 2. xxx(...) без Async => выполнение обычно идёт в потоке, который завершил предыдущую стадию 3. Для thenCombine(...) => колбэк выполняет поток, который добил вторую зависимость На собесе тут проверяют не знание API наизусть, а понимание модели и где поток меняется. Если ты отвечаешь «всё в одном executor», значит CompletableFuture ты знаешь на уровне джуниора Ну и навалите реакций, если хочется побольше таких вот постов