【问题标题】:How does JVM notify a thread blocked by `join()`?JVM如何通知被`join()`阻塞的线程?
【发布时间】:2019-09-26 04:38:17
【问题描述】:

join() 方法等待线程终止。它使用wait 来执行此操作。

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

那么当线程退出时,它如何通知等待集中的线程。

  1. 我尝试在 JDK 源代码中查找代码,但失败了。谁能给我看一下相关代码sn-ps?

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

  3. 如果isAlive() 为假,它只是返回,该线程已经处于等待设置中。 while(isAlive()) 有必要吗?

【问题讨论】:

    标签: java multithreading join jvm notify


    【解决方案1】:

    1) 我尝试在 JDK 源代码中查找代码,但失败了。谁能给我看一下相关代码sn-ps?

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

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

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

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

    这是不正确的。

    • “等待设置”参数不正确。如果当前线程可以调用isAlive(),则它不在any 等待集中。只有在 wait(...) 调用中时,它才会在目标 Thread 的“等待集”中。当通知当前线程时,它会从“等待集”中移除。

      重申一下,当t1 正在执行t2.wait(...) 时,一个线程t1 处于另一个线程t2 的“等待集”中。

    • 具有零超时的wait(...) 调用意味着“等待通知没有超时”。因此,这不是一个繁忙的循环。

    • 循环通常只会绕零或绕一圈。 (但请参阅我的答案的下一部分。)

    3) 如果 isAlive() 为假,它只是返回,该线程已经处于等待集合中。 while(isAlive()) 有必要吗?

    • 您的“等待设置”逻辑不正确(如上)。

    • 循环是必要的。任何引用目标Thread 对象的应用程序代码都可以在其上调用Object.notify()。这会导致 wait(0) 返回。但由于这种“唤醒”是虚假的,因此有必要检查目标 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 的位置。

    【讨论】:

      【解决方案2】:

      回答您的问题:

      1. wait() 是本机方法并使用系统代码。没有 Java 代码。

      2. wait() 不是等待线程的方法,而是在某个对象上进行同步。 Wait()是暂停线程的错误方法,需要使用sleep()。

      3. wait() 的对应物是 notify() 或 notifyAll()。这将唤醒等待调用对象的线程。 Wait() 和 notify 是 Object.class 的一部分,需要在对象上进行同步。

      只要它的 run 方法正在执行,一个线程就处于活动状态。如果你加入一个线程,调用线程将自动停止。 如果您想让线程等待,请使用 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();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-10-08
        • 2013-11-16
        • 2012-01-25
        • 1970-01-01
        • 1970-01-01
        • 2014-01-20
        • 1970-01-01
        相关资源
        最近更新 更多