Комментарии (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 потоке может заблокировать приложение