← Назад к вопросам

Что такое метод join()?

1.0 Junior🔥 161 комментариев
#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое метод join()?

Метод join() — это один из фундаментальных методов класса Thread для синхронизации между потоками. Он позволяет одному потоку ждать завершения другого потока.

Определение

join() — это метод экземпляра Thread, который заставляет вызывающий поток ждать, пока целевой поток не завершит свое выполнение.

Синтаксис

public final void join() throws InterruptedException
public final void join(long millis) throws InterruptedException
public final void join(long millis, int nanos) throws InterruptedException

Пример базового использования

public class JoinExample {
    public static void main(String[] args) throws InterruptedException {
        Thread worker = new Thread(() -> {
            System.out.println("Worker started");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("Worker finished");
        });
        
        worker.start();
        System.out.println("Main: ожидаю завершения worker");
        worker.join(); // Блокирует main поток
        System.out.println("Main: worker завершился");
    }
}

Вывод:

Worker started
Main: ожидаю завершения worker
Worker finished
Main: worker завершился

Как это работает

Когда вызывается join():

  1. Вызывающий поток входит в состояние WAITING
  2. Целевой поток продолжает выполняться
  3. Когда целевой поток завершается, вызывающий поток просыпается
  4. Выполнение вызывающего потока продолжается

join() с timeout

Thread worker = new Thread(() -> {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

worker.start();

// Ждем максимум 2 секунды
worker.join(2000);

if (worker.isAlive()) {
    System.out.println("Worker все еще работает");
} else {
    System.out.println("Worker завершился");
}

Практический сценарий: параллельная загрузка данных

public class DataLoader {
    public static void main(String[] args) throws InterruptedException {
        List<Thread> loaders = new ArrayList<>();
        
        // Создаем потоки для загрузки данных из разных источников
        Thread loader1 = new Thread(() -> loadFromDatabase());
        Thread loader2 = new Thread(() -> loadFromAPI());
        Thread loader3 = new Thread(() -> loadFromCache());
        
        loaders.add(loader1);
        loaders.add(loader2);
        loaders.add(loader3);
        
        // Запускаем все параллельно
        for (Thread loader : loaders) {
            loader.start();
        }
        
        // Ждем все потоки
        for (Thread loader : loaders) {
            loader.join();
        }
        
        System.out.println("Все данные загружены");
    }
    
    static void loadFromDatabase() {
        System.out.println("Loading from DB");
    }
    
    static void loadFromAPI() {
        System.out.println("Loading from API");
    }
    
    static void loadFromCache() {
        System.out.println("Loading from Cache");
    }
}

InterruptedException

join() выбрасывает InterruptedException, если поток был прерван:

try {
    worker.join();
} catch (InterruptedException e) {
    System.out.println("Main поток был прерван");
    Thread.currentThread().interrupt();
}

Важные замечания

  1. join() — это блокирующая операция - вызывающий поток ждет
  2. Нельзя вызвать join() на текущем потоке - приведет к deadlock
  3. join() выбрасывает checked exception - нужно обработать
  4. Несколько потоков могут ждать один поток - все получат notification

Альтернативы в современной Java

Вместо join() часто используют:

// ExecutorService
ExecutorService executor = Executors.newFixedThreadPool(3);
Future<?> future = executor.submit(() -> doWork());
future.get(); // Ждет завершения

// CountDownLatch
CountDownLatch latch = new CountDownLatch(1);
new Thread(() -> {
    doWork();
    latch.countDown();
}).start();
latch.await(); // Ждет

// CompletableFuture
CompletableFuture.runAsync(() -> doWork())
    .join(); // Другой join()!

Когда использовать join()

  • Простые сценарии с несколькими потоками
  • Школьные проекты и примеры
  • Когда не нужна гибкость ExecutorService

Когда избегать join()

  • Множество потоков (используй ExecutorService)
  • Нужна обработка результатов (Future, CompletableFuture)
  • Нужна гибкость с timeout и interruption