Baeldung 翻译系列之Java并发基础:
Java中的concurrent包
Java中的Synchronized关键字
Future介绍
ThreadLocal介绍
Java线程的生命周期
如何杀掉一个Java线程
Java中的线程池介绍
实现Runnable接口还是继承Thread类
Java中的wait和notify方法
Runnable vs Callable
wait和sleep的区别
Thread.join方法介绍
Java中使用锁对象
ThreadPoolTaskExecutor中的corePoolSize和maxPoolSize
Java中的异步编程
1. 概览
在本教程中,我们将讨论Thread
类中不同的join()
方法。我们将详细介绍这些方法,并提供一些示例代码。
和wait()
和notify()
方法一样,join()
是线程间同步的另一种机制。
2. Thread.join()方法
join方法在Thread类中定义如下:
public final void join() throws InterruptedException
Waits for this thread to die.
当我们在一个线程上调用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("Inside Thread " + this.getName() + ", processingCount = " + 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());
}
在执行代码时,我们应该期望类似于以下的结果:
[main] INFO: Thread Thread-1 Created
[main] INFO: Invoking join
[Thread-1] INFO: Thread Thread-1 started
[Thread-1] INFO: Inside Thread Thread-1, processingCount = 0
[Thread-1] INFO: Thread Thread-1 exiting
[main] INFO: Returned from join
join()
方法还可能在被引用的线程被中断时返回。在这种情况下,该方法会抛出InterruptedException
异常。
最后,如果被引用的线程已经终止或尚未启动,调用join()
方法会立即返回。
Thread t1 = new SampleThread(0);
t1.join(); //returns immediately
3. 带Timeout的Thread.join方法
如果被引用的线程被阻塞或处理时间过长,join()
方法将一直等待。这可能会成为一个问题,因为调用线程将变得无响应。为了处理这些情况,我们可以使用join()
方法的重载版本来指定超时时间。
join()
方法有两个重载的定时版本:
public final void join(long millis) throws InterruptedException
//在最多等待 millis 毫秒的时间内,等待该线程终止。超时时间为 0 表示无限等待。
public final void join(long millis,intnanos) throws InterruptedException
//在最多等待 millis 毫秒加上 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()
方法将返回控制权给调用方法。
定时的join()
方法的等待时间取决于操作系统的计时机制。因此,我们不能假设join()
方法会精确地等待指定的时间。
4. 同步中的Thread.join方法
除了等待线程终止外,调用join()
方法还具有同步效果。join()
方法创建了一个 happens-before
关系:
一个线程中的所有操作都发生在任何其他线程成功从该线程的join()返回之前, A 发生在B之前,那么就是我们说的 A happens-before B
这意味着当线程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);
为了正确同步上面的代码,我们可以在循环内部添加定时的t4.join()
,或者使用其他同步机制。
5. 总结
join()
方法在线程间同步中非常有用。在本文中,我们讨论了join()
方法及其行为。我们还回顾了使用join()
方法的代码。
评论区