JVM 如何通知被 `join()` 阻塞的线程?

How does JVM notify a thread blocked by `join()`?

join() 方法等待线程死亡。它使用 wait 来做到这一点。

if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        }

那么当线程退出时,如何通知wait set中的线程。

  1. 我试图在 JDK 源代码中查找代码,但失败了。谁能告诉我相关的代码片段?

  2. 当一个线程处于等待状态时,它可能会检查 isAlive() 这么多次它的时间片,这是一种浪费吗?

  3. 如果 isAlive() 为假,它只是 return,该线程已经在等待集中。 while(isAlive()) 有必要吗?

回答您的问题:

  1. wait() is a native method and uses System code. There is no Java code for that.

  2. wait() is not a means to wait for a Thread but to synchronize on a certain object. Wait() is the wrong method to pause a thread, you need to use sleep().

  3. The counterpart of wait() is notify() or notifyAll(). This will wake up Threads which wait for the calling object. Wait() and notify are part of the Object.class and need a synchronization on the object.

只要其 运行 方法正在执行,线程就处于活动状态。如果你加入一个线程,调用线程将自动停止。 如果你想让一个线程等待然后使用 Thread.sleep.

Thread t1 = new Thread(){
  public void run(){
     try {
        sleep(5000);
     } catch (InterruptedException e){
       e.printStackTrace();
     }
     System.out.println("I'm done");
  }
}

t1.start();
//The calling thread will wait here for 5 sec.
t1.join();

1) I try to find code in JDK source code, but failed. Can anyone show me the relevant code snippets?

OpenJDK jdk8u 源代码树中 Thread class 的路径名是 jdk/src/share/classes/java/lang/Thread.javajoin() 的代码如下。

notifyAll 出现的本机代码在 hotspot/src/share/vm/runtime/thread.cpp 中的 Thread::exit 中。

对于其他版本,路径可能不同。 (find 命令是你的朋友。)

2) When a thread in wait set, it may check isAlive() so many times for its timeslice, is this a waste?

这是不正确的。

  • "wait set" 参数不正确。如果当前线程可以调用 isAlive() 它不在 any 等待集中。当目标 Threadwait(...) 调用中时,它只会出现在 "wait set" 中。当通知当前线程时,它会从 "wait set" 中删除。

    重申一下,当 t1 正在执行 t2.wait(...).

    时,线程 t1 位于另一个线程 t2 的 "wait set" 中
  • 零超时的 wait(...) 调用意味着 "wait until notified without a timeout"。因此,这不是一个繁忙的循环。

  • 循环将通常循环零次或仅一次。 (但请参阅我的回答的下一部分。)

3) If isAlive() is false, it just return, that thread is already in wait set. Is the while(isAlive()) necessary?

  • 你的"wait set"逻辑不正确(如上)。

  • 循环是必要的。任何引用目标 Thread 对象的应用程序代码都可以对其调用 Object.notify()。这导致 wait(0) 变为 return。但是因为这个 "wake up" 是虚假的,所以有必要检查目标 Thread 是否真的结束了(通过调用 isAlive())并且可能再次等待。

    可能重复发生...如果应用程序代码在做一些愚蠢的事情...但它不应该。


public final synchronized void join(long millis)
throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

Thread 的大部分实现都在本机代码中。这就是唤醒加入线程的 notifyAll 的地方。