侧边栏壁纸
博主头像
翻斗

开始一件事最好是昨天,其次是现在

  • 累计撰写 44 篇文章
  • 累计创建 42 个标签
  • 累计收到 3 条评论

Thread中的join方法

翻斗
2020-04-11 / 0 评论 / 0 点赞 / 824 阅读 / 3,034 字

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()方法的代码。

如往常一样,完整的源代码可以在GitHub 或者 Gitee上找到。

0

评论区