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

Что делает join?

1.0 Junior🔥 231 комментариев
#Базы данных и SQL

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

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

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

Что делает join()?

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

Базовое использование

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

// Вывод:
// Главный ждёт...
// Рабочий начал
// Рабочий завершил
// Рабочий закончил, главный продолжает

Сигнатуры методов

public final void join() throws InterruptedException
// Ждёт бесконечно (пока поток не завершится)

public final void join(long millis) throws InterruptedException
// Ждёт до millis миллисекунд

public final void join(long millis, int nanos) throws InterruptedException
// Ждёт до millis миллисекунд + nanos наносекунд

Сценарий с таймаутом

public class JoinWithTimeout {
    public static void main(String[] args) throws InterruptedException {
        Thread slowWorker = new Thread(() -> {
            System.out.println("Работник начал");
            try {
                Thread.sleep(5000); // долгая операция
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Работник закончил");
        });
        
        slowWorker.start();
        System.out.println("Главный ждёт максимум 2 секунды");
        
        slowWorker.join(2000); // ждём только 2 секунды
        
        // После 2 секунд главный продолжит, даже если worker не закончил
        if (slowWorker.isAlive()) {
            System.out.println("Время истекло, worker ещё работает");
        }
    }
}

// Вывод:
// Главный ждёт максимум 2 секунды
// Работник начал
// Время истекло, worker ещё работает
// Работник закончил (позже)

Обработка InterruptedException

public class JoinInterruptionExample {
    public static void main(String[] args) throws InterruptedException {
        Thread worker = new Thread(() -> {
            System.out.println("Рабочий работает");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                System.out.println("Рабочий был прерван");
            }
        });
        
        worker.start();
        Thread.sleep(1000);
        worker.interrupt(); // прерывает ожидающий в sleep поток
        
        worker.join(); // ждём завершения
        System.out.println("Готово");
    }
}

Множественные потоки

public class MultipleThreadJoin {
    public static void main(String[] args) throws InterruptedException {
        Thread[] workers = new Thread[3];
        
        for (int i = 0; i < 3; i++) {
            final int threadId = i;
            workers[i] = new Thread(() -> {
                System.out.println("Поток " + threadId + " начал");
                try {
                    Thread.sleep((long) (Math.random() * 2000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Поток " + threadId + " завершил");
            });
            workers[i].start();
        }
        
        // Ждём завершения всех потоков
        for (Thread worker : workers) {
            worker.join();
        }
        
        System.out.println("Все потоки завершены");
    }
}

// Вывод:
// Поток 0 начал
// Поток 1 начал
// Поток 2 начал
// Поток 0 завершил
// Поток 1 завершил
// Поток 2 завершил
// Все потоки завершены

Практический пример: Map-Reduce паттерн

public class MapReduceExample {
    public static void main(String[] args) throws InterruptedException {
        int[] data = {1, 2, 3, 4, 5, 6, 7, 8};
        int[] partialSums = new int[2];
        
        // Создаём 2 рабочих потока
        Thread worker1 = new Thread(() -> {
            // Считаем сумму первой половины
            for (int i = 0; i < 4; i++) {
                partialSums[0] += data[i];
            }
        });
        
        Thread worker2 = new Thread(() -> {
            // Считаем сумму второй половины
            for (int i = 4; i < 8; i++) {
                partialSums[1] += data[i];
            }
        });
        
        worker1.start();
        worker2.start();
        
        worker1.join(); // ждём завершения первого
        worker2.join(); // ждём завершения второго
        
        int totalSum = partialSums[0] + partialSums[1];
        System.out.println("Общая сумма: " + totalSum);
    }
}

Важные моменты

  1. Блокирующая операция: join() блокирует текущий поток до завершения целевого
  2. Исключение: throws InterruptedException — всегда нужно обрабатывать
  3. Порядок выполнения: join() гарантирует порядок (Memory Visibility через happens-before)
  4. Не использовать с главным потоком: может привести к deadlock
  5. Альтернативы: CountDownLatch, CyclicBarrier, Phaser

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

  • Ожидание завершения рабочих потоков
  • Синхронизация stages вычисления
  • Shutdown graceful закрытие приложения
  • Тестирование многопоточного кода
join() — это простой и надёжный способ синхронизации потоков, хотя для сложных сценариев часто используют более специализированные инструменты из java.util.concurrent.
Что делает join? | PrepBro