【问题标题】:Query regarding thread dead or leak关于线程死或泄漏的查询
【发布时间】:2014-08-20 13:02:06
【问题描述】:

假设在我的应用程序中有一个线程正在执行函数说:

public void function(){
.
.
.
try{
//do something
}catch(Exception ex){
throw ex;
}

}

现在线程每次执行函数时,都会去catch块,然后抛出异常ex,甚至父函数也会向其父函数抛出异常。 现在的问题是我的线程会死掉还是会导致 mu 应用程序中的线程泄漏?

【问题讨论】:

  • 只返回不要抛出任何会结束线程的东西
  • run() 方法结束/返回时线程将停止执行,无论它调用的其他方法如何结束。就资源而言,您需要手动处理(关闭)它们。
  • 那么在这种情况下,你的意思是线程不会消亡并且会继续持有资源?
  • 未捕获(或捕获)的异常不会改变线程的生命周期。代码将抛出异常,并假设您没有配置处理程序来捕获它,系统的默认处理程序会将其打印到 sys.out 并退出线程。没有什么会消亡或被杀死。如果你在 try 中打开了一些文件或套接字,但在关闭它之前抛出了异常,你应该使用 finally 块来关闭它们。想象一下你的主线程方法中的相同情况以及你在那里处理它们的方式,你也应该在这里做。
  • @user3363969 - 不,线程将在 run 完成时终止

标签: java multithreading memory-leaks


【解决方案1】:

线程将正常终止,但问题是它不会释放异常发生时它所持有的任何资源。

例子:

public class TryThreads {
static void someMethod() throws Exception {
    System.out.println("hello");
    throw new Exception();
}

public static void main(String[] args) throws InterruptedException {

    Thread T = new Thread(new Runnable() {

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getState());
            try {
                someMethod();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("still here");
        }

    });
    T.start();
    Thread.currentThread().sleep(1000);
    System.out.println(T.getState());
}
}

O/P:

RUNNABLE
hello
java.lang.Exception
    at TryThreads.someMethod(TryThreads.java:4)
    at TryThreads$1.run(TryThreads.java:15)
    at java.lang.Thread.run(Unknown Source)
still here
**TERMINATED** --> this line

【讨论】:

    【解决方案2】:

    用户代码未捕获的异常最终会被线程的 run() 函数抛出。这会导致线程终止执行。

    【讨论】:

      【解决方案3】:

      在 Java 中,线程需要进行垃圾回收。没有必要(或方式)手动释放(内部)资源。

      只有当你积累对你的线程的引用并且从不释放它们时,你才会创建一个线程泄漏。这意味着:

      // cool, nobody references the thread, let the gc collect it when done
      void foo() {
        Thread t = new Thread(new Runnable { ... });
        t.start();
      }
      
      // not cool, maybe set "t = null" some time
      private Thread t = new Thread(new Runnable { ... });
      void foo() {
        t.start();
      }
      

      另外请注意,我没有理由认为 JVM 在完成其用途后应该保留其内部资源(例如 pthread_t)的 java.lang.Thread。因此,一般来说,除了 JMM 和首先手动管理线程之外,您无需特别担心。为什么不使用ExecutorService

      【讨论】:

      • 线程打开的资源、文件、连接呢? JVM 没有关闭它们对吗?
      • 正确。对于这些,您应该使用 Java 7 的 try-with-resource 语句。另外不要忘记即使在单线程代码中也应该关闭资源。
      【解决方案4】:

      VM 在内存中为每个非守护线程保存一个线程控制块(一个小数据结构)。这通常不会被删除,直到另一个线程“加入”该线程(即对其发出 join() 调用以获得终止状态,该状态保存在该控制块中)。如果您的代码没有通过加入线程来清理这些控制块,那么从技术上讲,您有资源泄漏。如果 VM 的控制块数量有限,您最终可以将它们全部用完。

      通常的解决方案是在启动线程之前将它们设置为守护进程。这基本上告诉虚拟机您对终止状态不感兴趣,并且不需要通过调用 join() 与线程的终止同步,并且当它们终止时,虚拟机将为您清理控制块。

      【讨论】:

      • 你的说法既错误又危险。尤其是关于守护线程的部分。请注意,JVM 不必执行 finally 守护线程块,也不会展开堆栈。当您退出 JVM 时,它们将被简单地杀死。这是一个非常重要的细节。
      • 在 VM 终止的情况下,是的 - 当没有更多的非守护线程并且守护线程与 VM 进程一起“死亡”时会发生这种情况。但是,我并不认为这是这里的问题。在正常运行下,VM 将始终执行“finally”块并以与非守护线程相同的方式处理异常。如果有其他对它们的引用,线程对象也可能导致泄漏(有人已经在另一个答案中提到过)。我试图说明的一点是,VM 可以为非守护线程保存资源,而您的代码需要清理这些资源。
      • 你能指出那个答案吗?你到底在说什么样的泄漏? VM 为线程保留的资源未指定,您作为用户无法清理。我们不是在谈论可关闭的资源。哎呀,你甚至可以注册一个 UncaughtExceptionHandler 并在不同的地方清理它们。
      • 实际上它在您的回答中:“只有当您累积对线程的引用并且从不释放它们时,您才会创建线程泄漏。”至于另一部分,我们遇到了服务器应用程序线程用完(即无法再创建)的问题,因为它没有“加入”旧线程。
      • 是的。对象本身,就像任何其他受 GC 影响的引用类型一样,如果你不断累积对它的不必要引用,就会“泄漏”。是否以及如何实现线程根本不在规范中。您甚至可以在 JVM 实现中使用绿色线程。
      猜你喜欢
      • 2020-04-06
      • 1970-01-01
      • 2022-10-05
      • 2014-10-29
      • 2011-09-13
      • 2013-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多