Рубрики
Без рубрики

Метод Thread.join() в Java

Узнайте, как использовать методы Thread.join() для синхронизации потоков.

Автор оригинала: baeldung.

1. Обзор

В этом уроке мы обсудим различные методы join() в классе Thread . Мы подробно рассмотрим эти методы и некоторые примеры кода.

Как и методы wait() и notify () , join() является еще одним механизмом синхронизации между потоками.

Вы можете быстро просмотреть этот учебник , чтобы узнать больше о wait() и notify() .

2. Метод Thread.join()

Метод соединения определен в классе Thread :

public final void join() вызывает исключение InterruptedException Ждет, пока этот поток умрет.

Когда мы призываем присоединиться() метод в потоке, вызывающий поток переходит в состояние ожидания. Он остается в состоянии ожидания до тех пор, пока указанный поток не завершится.

Мы можем увидеть это поведение в следующем коде:

class SampleThread extends Thread {
    public int processingCount = 0;

    SampleThread(int processingCount) {
        this.processingCount = processingCount;
        LOGGER.info("Thread Created");
    }

    @Override
    public void run() {
        LOGGER.info("Thread " + this.getName() + " started");
        while (processingCount > 0) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                LOGGER.info("Thread " + this.getName() + " interrupted");
            }
            processingCount--;
        }
        LOGGER.info("Thread " + this.getName() + " exiting");
    }
}

@Test
public void givenStartedThread_whenJoinCalled_waitsTillCompletion() 
  throws InterruptedException {
    Thread t2 = new SampleThread(1);
    t2.start();
    LOGGER.info("Invoking join");
    t2.join();
    LOGGER.info("Returned from join");
    assertFalse(t2.isAlive());
}

При выполнении кода следует ожидать результатов, аналогичных следующим:

INFO: Thread Created
INFO: Invoking join
INFO: Thread Thread-1 started
INFO: Thread Thread-1 exiting
INFO: Returned from join

Метод join() также может вернуться, если указанный поток был прерван . В этом случае метод вызывает исключение InterruptedException .

Наконец, если указанный поток уже был завершен или не был запущен, вызов метода join() немедленно возвращает .

Thread t1 = new SampleThread(0);
t1.join();  //returns immediately

3. Методы Thread.join() с таймаутом

Метод join() будет продолжать ждать, если указанный поток заблокирован или обработка занимает слишком много времени. Это может стать проблемой, так как вызывающий поток перестанет реагировать. Чтобы справиться с этими ситуациями, мы используем перегруженные версии метода join () , которые позволяют нам указать период ожидания.

Есть два синхронизированные версии которые перегружают присоединиться() метод:

“public final void join(long millis ) вызывает исключение InterruptedException Ожидает не более миллисекунд, пока этот поток не умрет. Тайм-аут 0 означает вечное ожидание.”

“public final void join(long millis,int |/nanos ) вызывает исключение InterruptedException Ожидает не более миллисекунд плюс наносекунд наносекунд, чтобы этот поток умер.”

Мы можем использовать функцию join () , как показано ниже:

@Test
public void givenStartedThread_whenTimedJoinCalled_waitsUntilTimedout()
  throws InterruptedException {
    Thread t3 = new SampleThread(10);
    t3.start();
    t3.join(1000);
    assertTrue(t3.isAlive());
}

В этом случае вызывающий поток ожидает завершения потока t3 примерно в течение 1 секунды. Если поток t3 не завершается в этот период времени, метод join() возвращает управление вызывающему методу.

Приурочено присоединиться() зависит от операционной системы для синхронизации. Таким образом, мы не можем предположить, что присоединиться() будет ждать ровно столько, сколько указано.

4. Методы Thread.join() и синхронизация

В дополнение к ожиданию завершения, вызов метода join() имеет эффект синхронизации. join() создает отношения происходит до :

“Все действия в потоке происходят до того, как любой другой поток успешно вернется из join() в этом потоке.”

Это означает, что когда поток t1 вызывает t2.join (), все изменения, сделанные t2, видны в t1 при возврате. Однако, если мы не вызываем join() или не используем другие механизмы синхронизации, у нас нет никакой гарантии, что изменения в другом потоке будут видны текущему потоку, даже если другой поток завершен.

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

Ниже приведен пример неправильно синхронизированного кода:

SampleThread t4 = new SampleThread(10);
t4.start();
// not guaranteed to stop even if t4 finishes.
do {
       
} while (t4.processingCount > 0);

Чтобы правильно синхронизировать приведенный выше код, мы можем добавить timed t4.join() внутри цикла или использовать какой-либо другой механизм синхронизации.

5. Заключение

метод join() весьма полезен для синхронизации между потоками. В этой статье мы обсудили методы join() и их поведение. Мы также рассмотрели код с использованием метода join () .

Как всегда, полный исходный код можно найти на GitHub .