Java中的Thread.join()方法

2023/06/07

1. 概述

在本教程中,我们将讨论Thread类中的不同join()方法。我们将详细介绍这些方法和一些示例代码。

与wait()和notify()方法一样,join()是线程间同步的另一种机制。

你可以快速浏览本教程以了解有关wait()和notify()的更多信息。

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;

    SampleThread(int processingCount) {
        this.processingCount = processingCount;
        LOGGER.debug("Thread " + this.getName() + " created");
    }

    @Override
    public void run() {
        LOGGER.debug("Thread " + this.getName() + " started");
        while (processingCount > 0) {
            try {
                Thread.sleep(1000); // Simulate some work being done by thread
            } catch (InterruptedException e) {
                LOGGER.debug("Thread " + this.getName() + " interrupted.");
            }
            processingCount--;
            LOGGER.debug("Inside Thread " + this.getName() + ", processingCount = " + processingCount);
        }
        LOGGER.debug("Thread " + this.getName() + " exiting");
    }
}

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

执行代码时,我们应该期望得到类似于以下的结果:

DEBUG ... - Thread Thread-3 created
DEBUG ... - Invoking join.
DEBUG ... - Thread Thread-3 started
DEBUG ... - Inside Thread Thread-3, processingCount = 0
DEBUG ... - Thread Thread-3 exiting
DEBUG ... - Returned from join

如果引用的线程被中断,join()方法也可能返回。在这种情况下,该方法抛出InterruptedException。

最后,如果引用的线程已经终止或尚未启动,则对join()方法的调用将立即返回

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

3. 带超时的Thread.Join()

如果引用的线程被阻塞或处理时间过长,则join()方法将继续等待。这可能会成为一个问题,因为调用线程将变得无响应。为了处理这些情况,我们使用join()方法的重载版本,它允许我们指定超时期限。

join()方法有两种超时版本的重载

public final void join(long millis) throws InterruptedException:等待此线程死亡的时间最多为millis毫秒。0意味着永远等待。

public final void join(long millis,int nanos) throws InterruptedException:等待此线程死亡的时间最多为millis毫秒加nanos纳秒。

我们可以按如下方式使用超时版本的join():

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

在这种情况下,调用线程等待线程t3完成大约1秒。如果线程t3在此时间段内没有完成,则join()方法将控制权返回给调用方法。

超时版本的join()取决于操作系统的计时。因此,我们不能假设join()会等待指定的时间

4. Thread.join()和同步

除了等待终止之外,调用join()方法还有一个同步效果。join()创建一个“happens-before”关系

线程中的所有操作发生在任何其他线程从该线程上的join()成功返回之前。

这意味着当线程t1调用t2.join()时,t2所做的所有更改在返回时在t1中可见。但是,如果我们不调用join()或使用其他同步机制,则无法保证即使其他线程已完成,当前线程也能看到其他线程中的更改。

因此,即使对处于终止状态的线程调用join()方法会立即返回,但在某些情况下我们仍然需要调用它。

我们可以在下面看到一个不正确同步代码的示例:

@Test
@Disabled
void givenThreadTerminated_checkForEffect_notGuaranteed() {
    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()方法的代码。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章