【问题标题】:Why doesn't daemon thread which is joined to the main() thread die when main() thread execution ends?为什么 main() 线程执行结束时加入到 main() 线程的守护线程不会死?
【发布时间】:2015-05-04 10:59:18
【问题描述】:

在下面的代码中:

class Worker extends Thread {
    Thread t;

    public Worker(Thread thread) {
        t=thread;
    }

    public void run() {
        try {
            t.join();
        } catch(InterruptedException e) {
            System.out.println("Exception is thrown and caught");
        }
        System.out.println(Thread.activeCount());
        System.out.print("|work|");
    }

    public static void main(String[] args) {
        Thread t=Thread.currentThread();
        Worker worker = new Worker(t);
        worker.setDaemon(true);
        worker.start();
        System.out.println("Exit from main method");
    }
}

由于 worker 是加入到 main() 线程上的守护线程,|work|永远不应该打印,因为用户线程 main() 首先完成,并且由于 worker 是一个守护线程,所以当 main() 线程死亡时它也会停止。 但是,我得到的输出如下: 退出主方法 1 |工作|

请为我澄清这个问题。

在多次执行程序后,我观察到以下不同的输出:

没有 Thread.sleep(1000) :

退出主方法 2

退出主方法 1 |工作|

退出主方法 2 |工作|

使用 Thread.sleep(1000) :

退出主方法 2 |工作|

退出主方法

注意第一个输出没有 sleep() 方法。 |工作|未打印,但线程数显示为 2。这是否意味着 main() 线程执行在 Thread.activeCount() 之后但 |work| 之前结束是印刷的?在第三个输出中似乎 main() 在执行这两个语句后结束。

现在,我没想到 Thread.activeCount() 会是 2,因为守护线程 worker 加入到用户线程 main() 上,这意味着当 Thread.activeCount() 执行时,只会有工作线程和没有 main() 线程。

【问题讨论】:

  • 我认为守护线程的意义在于它们是安全的“松散”;他们不需要加入。
  • InterruptedException 发生了吗?
  • 没有。它没。打印后程序正常终止|work|
  • 你怎么知道的?使用该代码,您可能无法分辨。对于该异常,您有一个空的 catch 块。
  • 您的程序将在所有非守护线程执行完毕后退出。你说 t.start() 和一个 Sysout 紧随其后。我认为您的 main() 甚至在 t.join 发生或发生某种调度之前就退出了,这使您的 Sysout 在 join 调用之前执行。你能执行几次(很多),看看如果你的行为改变了怎么办?在 t.start() 之后和 sysout 之前添加一个睡眠,看看会发生什么?

标签: java multithreading join daemon


【解决方案1】:

我不知道虚拟机如何知道最后一个非守护线程何时停止运行的确切细节,但我可以想象两种解决方案:

  • 后台定期轮询以查看是否所有非守护线程都已退出(我怀疑这是实际的解决方案)
  • 后台线程加入所有非守护线程,并在所有加入返回后退出 VM

在这两种情况下,可能会出现竞争条件,守护线程有时在主线程死亡后有时间再执行一些操作,有时则没有。

我执行了几次你的代码,有时在主线程退出后会打印一些东西,有时不会,这证实了我的理论。

当我在t.join() 之后添加Thread.sleep(100L) 调用时,除了“从主方法退出”之外什么都不会打印。

另外请注意,如果您查看在 VM 中运行的线程列表(例如使用调试器),其中一个被命名为“DestroyJavaVM”。给定名称,我猜这是退出 JVM 的线程,与剩余的守护线程并发,一旦最后一个非守护线程停止运行。

【讨论】:

  • Exit from main method 应打印以防您添加sleep(100L) :)
  • 打印出来的。除了来自主线程的字符串之外,什么都不会打印。
  • @JB Nizet 是的,它不输出 |work|即使我添加 sleep(1) 。我想在主线程执行完成后守护线程停止有一定的时间延迟,无论多么小。我想知道 JVM 在这方面是如何工作的。
  • @JBNizet 请检查我编辑的帖子和我得到的输出。
  • @user3678484 阅读 Thread.activeCount() 的 javadoc: 返回的值只是一个估计值,因为该方法在遍历内部数据结构时线程数可能会动态变化,并且可能会受到影响通过某些系统线程的存在。此方法主要用于调试和监控目的
【解决方案2】:

你如何运行你的代码?

当我从命令行运行您的示例时,我得到: 退出主方法 1 |工作|

我猜你可能不会打印出activeCount|work|,因为当所有非守护线程终止时,JVM 可以停止守护线程(这里唯一的一个是main 线程)。当满足该条件时,JVM 会终止剩余的守护线程并退出。

Thread javadoc says:

Java 虚拟机继续执行线程,直到发生以下任一情况:

  • 已调用 Runtime 类的退出方法,并且安全管理器已允许执行退出操作。
  • 不是守护线程的所有线程都已死亡,要么从调用 run 方法返回,要么抛出传播到 run 方法之外的异常。

虽然终止这些线程可能需要一些时间,因此您的守护线程可能有机会执行更多指令。当我在打印activeCount 后添加Thread.sleep(1000) 时,activeCount 仍会打印,但|work| 不会。

【讨论】:

  • 请检查我编辑的帖子与我得到的输出。
【解决方案3】:

让我首先尝试回答

的输出

没有 Thread.sleep(1000)

从主方法 2 退出 - 您必须记住您的 Worker 线程是一个守护进程,因此 JVM 不会等待它完成执行。它只关心完成非守护进程的主线程执行。 现在你得到了这个输出,因为主线程显示了“从主方法退出”,同时工作线程运行并显示 2 但不幸的是主线程完成并且 JVM 没有等待工作线程(作为一个守护进程) 因此没有显示“工作”。

退出主方法1 |work| - 这里有与上面相同的解释 但是工作线程很幸运..它有足够的时间来显示'|work|' 在JVM完成主线程并匆忙退出之前。

退出主方法2 |work| - 与前面的解释相同...不确定 关于为什么 active count 有时返回 2 有时返回 1。

你可以使用类似的逻辑来得出结论

使用 Thread.sleep(1000) 逻辑。

提示:您也可以使用语句检查线程 t 的状态 System.out.println("t 的状态:" + t.getState());就在之前 t.join()..你会得到很多 TERMINATED..这证明了主线程 这是 NON DAEMON 已经终止,所以没有加入工作的问题...... 太晚了……

【讨论】:

    猜你喜欢
    • 2015-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多