Как выполнить две задачи параллельно

Два Thread

Низкоуровневый вариант сохраняет идею исходной заметки:

Thread first = new Thread(this::loadUsers, "users-loader");
Thread second = new Thread(this::loadOrders, "orders-loader");
 
first.start();
second.start();
 
first.join();
second.join();

start() обоих потоков вызывается до join(). Если написать first.start(); first.join(); second.start(), задачи будут выполняться последовательно.

ExecutorService

В прикладном коде пул обычно безопаснее неограниченного создания потоков:

ExecutorService pool = Executors.newFixedThreadPool(2);
try {
    Future<UserData> users = pool.submit(this::loadUsers);
    Future<OrderData> orders = pool.submit(this::loadOrders);
 
    Report report = combine(users.get(), orders.get());
} finally {
    pool.shutdown();
}

Ошибку задачи get() вернет как ExecutionException, а прерывание ожидающего потока — как InterruptedException.

CompletableFuture

Для композиции независимых результатов:

CompletableFuture<UserData> users =
        CompletableFuture.supplyAsync(this::loadUsers, ioPool);
CompletableFuture<OrderData> orders =
        CompletableFuture.supplyAsync(this::loadOrders, ioPool);
 
CompletableFuture<Report> report =
        users.thenCombine(orders, this::combine);

Исполнитель лучше передавать явно: общий пул может одновременно обслуживать чужие parallel streams и CPU-задачи.

Ограничения

  • Параллелить можно только независимые операции.
  • Общие изменяемые данные требуют синхронизации или устранения совместного владения.
  • Две операции по 100 мс не гарантируют ответ за 100 мс: есть очереди, CPU, сеть и сборка результата.
  • При сбое одной ветки нужно определить отмену второй и общий deadline.
  • Для внешнего ресурса важен его лимит: 100 потоков не создадут 100 соединений, если пул БД равен 10.

На собеседовании

  • Где поставить join()? После запуска обеих задач.
  • Чем пул лучше new Thread на каждый вызов? Ограничивает ресурсы и переиспользует потоки.
  • Когда параллельность замедляет? Для мелких задач или при общем узком ресурсе.
  • Как получить два результата? Два Future либо thenCombine.
  • Что делать с общим тайм-аутом? Считать единый deadline, а не давать каждой ветке полный timeout.