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

Что такое join в многопоточности?

2.0 Middle🔥 201 комментариев
#Многопоточность

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

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

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

Join в многопоточности Java

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

Синтаксис и основной пример

public class JoinExample {
    public static void main(String[] args) throws InterruptedException {
        Thread worker = new Thread(() -> {
            try {
                System.out.println("Рабочий поток: начало работы");
                Thread.sleep(2000); // Имитация работы
                System.out.println("Рабочий поток: работа завершена");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        
        worker.start();
        
        System.out.println("Главный поток: ожидание рабочего потока");
        worker.join(); // Главный поток ждет завершения worker
        
        System.out.println("Главный поток: рабочий поток завершился");
    }
}
// Вывод:
// Главный поток: ожидание рабочего потока
// Рабочий поток: начало работы
// Рабочий поток: работа завершена
// Главный поток: рабочий поток завершился

Варианты join()

join() без параметров: ждёт неопределённо долго.

worker.join(); // Ждёт завершения рабочего потока

join(long millis): ждёт максимум N миллисекунд.

worker.join(5000); // Ждёт максимум 5 секунд
if (worker.isAlive()) {
    System.out.println("Поток всё ещё работает");
}

join(long millis, int nanos): ждёт с наносекундной точностью.

worker.join(1000, 500000); // 1.0005 секунд

Практический пример: параллельная обработка с ожиданием

public class ParallelDownload {
    public static void main(String[] args) throws InterruptedException {
        String[] urls = {"url1", "url2", "url3"};
        Thread[] threads = new Thread[urls.length];
        
        // Запустить загрузку параллельно
        for (int i = 0; i < urls.length; i++) {
            final int index = i;
            threads[i] = new Thread(() -> {
                System.out.println("Загрузка " + urls[index]);
                try {
                    Thread.sleep(2000);
                    System.out.println("Загружено " + urls[index]);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
            threads[i].start();
        }
        
        // Ждать завершения всех потоков
        for (Thread thread : threads) {
            thread.join();
        }
        
        System.out.println("Все загрузки завершены");
    }
}

Join с ExecutorService (современный подход)

public class ExecutorServiceExample {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(3);
        
        // Отправить задачи
        executor.submit(() -> System.out.println("Задача 1"));
        executor.submit(() -> System.out.println("Задача 2"));
        executor.submit(() -> System.out.println("Задача 3"));
        
        // Эквивалент join(): остановить приём новых задач и ждать
        executor.shutdown(); // Запретить новые задачи
        executor.awaitTermination(10, TimeUnit.SECONDS);
        
        System.out.println("Все задачи завершены");
    }
}

Join с timeout (не забудь обработать завершение по timeout)

public class JoinWithTimeout {
    public static void main(String[] args) throws InterruptedException {
        Thread worker = new Thread(() -> {
            try {
                Thread.sleep(10000); // 10 секунд
            } catch (InterruptedException e) {
                System.out.println("Поток прерван");
            }
        });
        
        worker.start();
        
        // Ждём максимум 3 секунды
        worker.join(3000);
        
        if (worker.isAlive()) {
            System.out.println("Поток всё ещё работает, прерываем");
            worker.interrupt();
            worker.join(); // Ждём завершения после interrupt
        } else {
            System.out.println("Поток завершился");
        }
    }
}

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

  • Координация потоков: дождаться завершения предыдущих работ
  • Собрание результатов: получить результаты из нескольких потоков
  • Пошаговая обработка: обработать данные в несколько этапов

Потенциальные проблемы

  • Deadlock: если потоки ждут друг друга в круг
  • InterruptedException: правильно обрабатывать прерывания
  • Производительность: join() на main потоке может заблокировать приложение