Метод Thread.join () в Java
1. обзор
В этом руководстве мы обсудим различные методыjoin() в классеThread. Мы подробно рассмотрим эти методы и некоторые примеры кода.
Подобноwait() иnotify() methods,join() - еще один механизм межпоточной синхронизации.
Вы можете быстро взглянуть наthis tutorial, чтобы узнать больше оwait() иnotify().
2. МетодThread.join()
Метод соединения определен в классеThread:
public final void join () выбрасывает InterruptedException Ожидание этой нити, чтобы умереть.
Когда мы вызываем методjoin() в потоке, вызывающий поток переходит в состояние ожидания. Он остается в состоянии ожидания, пока указанный поток не завершится.
Мы можем видеть это поведение в следующем коде:
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
The join() method may also return if the referenced thread was interrupted. В этом случае метод выдаетInterruptedException.
Наконец,if the referenced thread was already terminated or hasn’t been started, the call to join() method returns immediately.
Thread t1 = new SampleThread(0);
t1.join(); //returns immediately
3. Thread.join() Методы с тайм-аутом
Методjoin() будет продолжать ждать, если указанный поток заблокирован или обработка занимает слишком много времени. Это может стать проблемой, поскольку вызывающая нить перестает отвечать на запросы. Чтобы справиться с этими ситуациями, мы используем перегруженные версии методаjoin(), которые позволяют нам указать период ожидания.
Есть дваtimed versions, которые перегружают методjoin():
“public final void join(long millis_) выдает InterruptedException_Waits at most миллис milliseconds for this thread to die. A timeout of 0 means to wait forever.”
“public final void join(long millis, int nanos_) выбрасывает InterruptedException_Waits at most millis milliseconds plus nanos_ наносекунды, чтобы этот поток умер ». _
Мы можем использовать рассчитанные по времениjoin(), как показано ниже:
@Test
public void givenStartedThread_whenTimedJoinCalled_waitsUntilTimedout()
throws InterruptedException {
Thread t3 = new SampleThread(10);
t3.start();
t3.join(1000);
assertTrue(t3.isAlive());
}
В этом случае вызывающий поток ожидает примерно 1 секунду для завершения потока t3. Если поток t3 не завершается в этот период времени, методjoin() возвращает управление вызывающему методу.
Timedjoin() зависит от ОС для синхронизации. Таким образом, мы не можем предположить, чтоjoin() будет ждать точно столько, сколько указано.
4. Thread.join() Методы и синхронизация
Помимо ожидания завершения, вызов методаjoin() имеет эффект синхронизации. join() creates a happens-before relationship:
«Все действия в потоке происходят до того, как какой-либо другой поток успешно вернется из соединения () в этом потоке».
Это означает, что когда поток 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);
Чтобы правильно синхронизировать приведенный выше код, мы можем добавить timedt4.join() внутри цикла или использовать какой-либо другой механизм синхронизации.
5. Заключение
Методjoin() весьма полезен для межпоточной синхронизации. В этой статье мы обсудили методыjoin() и их поведение. Мы также рассмотрели код с использованием методаjoin().
Как всегда, полный исходный код можно найтиover on GitHub.