【问题标题】:Why does a thread outlive the main method in Java?为什么线程比 Java 中的 main 方法寿命更长?
【发布时间】:2012-12-03 23:50:07
【问题描述】:

我正在自学 Java 线程,我注意到一些让我有点困惑的东西。我创建了一个名为engine 的类来实现Runnable。 run 方法只是打印“Hello World”,休眠一秒钟,然后重复。

在我的主要方法中,我有:

public static void main(String[] args) {
    Thread thread = new Thread(engine);
    thread.start();
    System.out.println("Done.");
}

正如我所料,我看到了“Hello World”和“Done”。打印的很快,说明main方法已经结束了,但没想到我开始的线程在main结束后还在运行。

为什么程序在 main 退出后仍继续执行?我原以为当 main 退出时,进程会终止,所有线程都会被强制清理。这是否意味着必须明确地加入/终止每个线程才能使 Java 程序终止?

【问题讨论】:

  • 我的猜测是,当所有线程都完成时,程序就完成了。如果您的线程处于循环中,则程序在终止之前不会完成。
  • 只是为了确保我不会发疯(我总是在不考虑 C 的情况下编写连接代码),如果我在 C 程序中执行此操作,那么它会强制终止线程,对吗?
  • 来自实践中的并发“如果任何应用程序线程(守护程序或非守护程序)在关闭时仍在运行,它们将继续与关闭进程并发运行。...... JVM 不会尝试停止或中断任何在关闭时仍在运行的应用程序线程;当 JVM 最终停止时,它们会突然终止”。因此,您的线程有更多的事情要做(就像上面所说的 gsingh2011)

标签: java multithreading


【解决方案1】:

用户线程继续独立于其父线程(即创建者线程)的生命周期运行。因此,您必须在 main 线程终止之前通过调用 Thread.join 显式加入线程。

来自Javadoc of Thread

当 Java 虚拟机启动时,通常有一个 非守护线程(通常调用一些名为 main 的方法 指定类)。 Java 虚拟机继续执行 线程,直到发生以下任一情况:

  1. Runtime 类的退出方法已被调用,安全性 经理已允许执行退出操作。

  2. 所有不是守护线程的线程都已经死亡,或者通过返回 从调用 run 方法或抛出异常 传播到 run 方法之外。

如果您希望 JVM 终止,即使线程 t 正在运行,您应该 make thread t a daemon thread

t.setDaemon(true);

【讨论】:

  • 我最初专注于回答“这是否意味着必须明确地加入/终止每个线程才能使 Java 程序终止?”。然后,我将其扩展到“为什么”部分。
  • 谢谢 :) 只是为了确保我不会发疯(我总是在不考虑 C 的情况下编写连接代码),如果我在 C 程序中执行此操作,那么它将强制终止线程,对吗?
  • 不,t.join 强制调用join 的线程等待直到线程t 终止。 join 在 C (pthreads) 中具有相似的含义,但是,这是不同的问题。
  • 我明白我加入的是什么。我的意思是我认为如果没有执行连接,C 应用程序会终止,而 Java 应用程序会继续运行,直到线程完成,即使没有连接。但是谢谢;)
【解决方案2】:

如果您希望程序在 main 方法完成后退出,请考虑将您的线程设置为守护进程。但请注意,当 main 完成时,守护线程将被中止。
你可以像这样创建一个守护进程:

Thread t = new Thread(...);
t.setDaemon(true);

所有非守护线程都是用户线程。这些线程正在阻止 jvm 关闭。

【讨论】:

  • "当 main 退出时,守护线程将被中止"。仅当 所有 创建的线程都是守护进程时才如此。规则是,一旦唯一运行的线程是守护线程,JVM 就会退出,但守护进程当然可以比主线程存活。
  • 来自 Concurrency in Practice:“当一个线程退出时,JVM 会对正在运行的线程进行清点,如果剩下的唯一线程是守护线程,则它会启动有序关闭。当 JVM 停止时, 任何剩余的守护线程都被放弃了。最后不执行块,不展开堆栈 - JVM 只是存在。......将守护线程用于可能执行任何类型 I/O 的任务是危险的......最好为“家务”任务而保存”
【解决方案3】:

因为它就是这样工作的。当System.exit() 被调用,或者最后一个非守护线程停止运行时,程序退出。

这是有道理的。例如,如果没有这条规则,每个只包含生成 GUI 的 Java 程序都必须无限等待()以避免程序立即退出。

【讨论】:

  • 感谢您的回答,查看它的有用方式:) +1
【解决方案4】:

有两种类型的线程,用户和守护进程。当没有更多用户线程时,该进程终止。主线程始终是用户线程。您启动的线程也是用户线程,因此只要进程运行,它就会一直保持活动状态。

在您开始之前在您的线程上调用setDaemon(true) 将使您的进程在您的main() 函数返回时立即终止(或多或少)。

【讨论】:

    【解决方案5】:

    Java Language Specification section 12.8 表示:

    12.8。程序退出

    当发生以下两种情况之一时,程序会终止其所有活动并退出 发生:

    所有不是守护线程的线程都终止。

    某个线程调用了Runtime类或System类的exit方法, 并且退出操作未被安全管理器禁止。

    这意味着主线程完成是不够的。

    如果您确实希望它在主线程结束时退出,您需要使用 Thread#setDaemon 使新线程成为守护进程,或者按照您最初的建议使用 Thread#join

    【讨论】:

      【解决方案6】:

      主线程也是一个正在创建的用户线程,它的生命周期类似于任何其他用户线程。
      除非您将线程设置为守护线程,否则其他用户线程不依赖于主线程。 一旦主线程完成它的工作,它就会结束(它既不会结束其他用户线程,也不会因为其他用户线程正在运行而结束进程)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-04-27
        • 2013-10-22
        • 2013-07-14
        • 2020-10-25
        • 2016-03-29
        • 2011-07-10
        • 2018-12-20
        相关资源
        最近更新 更多