Вопрос 26 · Раздел 19

Что делает метод join() и чем он отличается от get()

У get() есть версия с таймаутом: get(5, TimeUnit.SECONDS). У join() — НЕТ таймаут-варианта. В production-коде, где нужен таймаут, используйте get() или оборачивайте join() через...

Версии по языкам: English Russian Ukrainian

🟢 Junior Level

join() и get() — оба ожидают завершения CompletableFuture и возвращают результат.

Главное отличие:

  • get() — выбрасывает checked исключения (InterruptedException, ExecutionException)
  • join() — выбрасывает unchecked (CompletionException)
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> "Hello");

// get() — нужен try-catch
try {
    String result = cf.get();  // throws checked exceptions
} catch (InterruptedException | ExecutionException e) {
    // handle
}

// join() — проще
String result = cf.join();  // throws CompletionException (unchecked)

🟡 Middle Level

Когда что использовать

get() — когда нужны checked исключения:

try {
    String result = cf.get(5, TimeUnit.SECONDS);  // с таймаутом
} catch (TimeoutException e) {
    // timeout handling
} catch (InterruptedException e) {
    // interrupt handling
}

Критичное практическое различие

У get() есть версия с таймаутом: get(5, TimeUnit.SECONDS). У join() — НЕТ таймаут-варианта. В production-коде, где нужен таймаут, используйте get() или оборачивайте join() через orTimeout().

join() — для цепочек и тестов:

// В цепочках
CompletableFuture.allOf(cf1, cf2, cf3)
    .thenApply(v -> {
        String r1 = cf1.join();  // не нужно try-catch
        String r2 = cf2.join();
        String r3 = cf3.join();
        return combine(r1, r2, r3);
    });

// В тестах
@Test
void test() {
    String result = cf.join();  // проще
    assertEquals("expected", result);
}

Типичные ошибки

  1. join() без обработки ошибок: ```java // ❌ Если CF упадёт — CompletionException String result = cf.join(); // может выбросить

// ✅ С обработкой cf.exceptionally(ex -> “fallback”) .join(); // безопасно


---

## 🔴 Senior Level

### Internal Implementation

```java
// Упрощённое представление. Реальные методы в JDK могут отличаться.
public T get() throws InterruptedException, ExecutionException {
    Object r = reportGet();
    if (r instanceof AltResult alt) {
        if (alt.ex != null) throw new ExecutionException((Throwable) alt.ex);
    }
    return (T) r;
}

public T join() {
    Object r = reportGet();
    if (r instanceof AltResult alt) {
        if (alt.ex != null) throw new CompletionException((Throwable) alt.ex);
    }
    return (T) r;
}

// Разница только в типе исключения!

Performance

get(): ~100 ns (checked exception handling)
join(): ~100 ns (same internals)

Разница negligible — выбор по удобству

Best Practices

// ✅ join() в цепочках
cf.thenApply(v -> cf2.join());

// ✅ join() в тестах
assertEquals("expected", cf.join());

// ✅ get(timeout) для production
cf.get(5, TimeUnit.SECONDS);

// ❌ join() без обработки ошибок
// ❌ get() без таймаута в production

🎯 Шпаргалка для интервью

Обязательно знать:

  • join() — unchecked CompletionException, get() — checked InterruptedException + ExecutionException
  • join() НЕТ таймаута, get(timeout, unit) ЕСТЬ таймаут
  • join() предпочтителен в цепочках и тестах, get(timeout) для production
  • Внутренне одинаковы — разница только в типе исключения
  • Оба блокируют поток до завершения CF

Частые уточняющие вопросы:

  • join() или get() — когда что? — join() в цепочках/тестах, get(timeout) в production
  • join() может выбросить TimeoutException? — Нет. Нужен orTimeout() перед join()
  • CompletionException vs ExecutionException? — CompletionException (join) — unchecked, ExecutionException (get) — checked
  • join() после allOf() блокирует? — Нет, если allOf() завершён. Но join() без allOf() — блокирует

Красные флаги (НЕ говорить):

  • «join() неблокирующий» — блокирует до завершения CF
  • «get() и join() полностью идентичны» — разные типы исключений, get имеет таймаут
  • «join() без timeout в production — ок» — бесконечное ожидание при ошибке

Связанные темы:

  • [[20. Как реализовать timeout для CompletableFuture]]
  • [[23. Как тестировать код с CompletableFuture]]
  • [[6. Как обрабатывать исключения в цепочке CompletableFuture]]
  • [[14. Что такое блокирующий код и как его отличить от неблокирующего]]