Опишите ваш опыт работы с многопоточностью в Java

«Опишите ваш опыт работы с многопоточностью в Java» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Опыт охватывает низкоуровневые и высокоуровневые API многопоточности:

  • Создание потоков: Thread, Runnable, Callable.
  • Управление пулами: ExecutorService, ForkJoinPool.
  • Асинхронные вычисления: Future, CompletableFuture.
  • Синхронизация: synchronized, ReentrantLock, ReadWriteLock, Semaphore.
  • Потокобезопасные коллекции: ConcurrentHashMap, CopyOnWriteArrayList, BlockingQueue.
  • Атомарные операции: volatile, AtomicInteger и другие классы из java.util.concurrent.atomic.

Ключевые решаемые проблемы: Race condition, deadlock, livelock, starvation.

Пример использования ExecutorService:

// Создание пула из 4 потоков
ExecutorService executor = Executors.newFixedThreadPool(4);
List<Future<Integer>> futures = new ArrayList<>();

// Отправка задач на выполнение
for (int i = 0; i < 10; i++) {
    futures.add(executor.submit(() -> {
        // Имитация работы задачи
        return ThreadLocalRandom.current().nextInt(100);
    }));
}

// Получение результатов
for (Future<Integer> f : futures) {
    try {
        System.out.println("Результат: " + f.get()); // get() блокирует поток
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
}

// Обязательное завершение работы пула
executor.shutdown();

Почему ExecutorService предпочтительнее new Thread()? Эффективное управление ресурсами (переиспользование потоков), контроль над количеством одновременно выполняемых задач.

Дополнительно: Опыт с CompletableFuture для построения асинхронных цепочек и комбинирования результатов, а также с ForkJoinPool для рекурсивного параллелизма (например, обход деревьев).