6

这个问题困扰了我一段时间,但我还没有找到完整的答案(例如,这个问题是用于 C#在 try/finally 外部或内部初始化一次性资源)。考虑以下两个 Java 代码片段:

Closeable in = new FileInputStream("data.txt");
try {
    doSomething(in);
} finally {
    in.close();
}

和第二个变体

Closeable in = null;
try {
    in = new FileInputStream("data.txt");
    doSomething(in);
} finally {
    if (null != in) in.close();
}

让我担心的部分是线程可能在获取资源的那一刻(例如打开文件)之间有些中断,但结果值没有分配给相应的局部变量。除了以下情况之外,线程是否有任何其他情况可能会在上述情况下被中断:

  1. 抛出 InterruptedException(例如通过 Thread#interrupt())或 OutOfMemoryError 异常
  2. JVM 退出(例如通过 kill、System.exit())
  3. 硬件故障(或 JVM 中的错误以获取完整列表 :)

我读过第二种方法更“惯用”,但在上述情况下,IMO 没有区别,在所有其他情况下它们都是平等的。

所以问题:

两者有什么区别?如果我确实担心释放资源(尤其是在大量多线程应用程序中),我应该更喜欢哪个?为什么?

如果有人指出支持答案的部分 Java/JVM 规范,我将不胜感激。

4

3 回答 3

6

我认为没有任何理由担心:

1) InterruptedException(例如通过 Thread#interrupt())

调用Thread.interrupt()不会导致InterruptedException自发抛出。异常仅在特定的(并且有据可查的)阻塞方法中引发;即阻塞 I/O 和同步方法。从流构造函数返回后,进入 try 块之前,不能抛出此异常。

或抛出 OutOfMemoryError 异常

如果OutOfMemoryError抛出 an ,则无论您将流构造函数放在何处,都不能保证完全恢复底层文件描述符。您永远不应该尝试从 OOM 中恢复,因此流是否关闭的问题没有实际意义。此外,此异常仅在实际尝试分配内存的线程上引发,此时尚未发生。

2) JVM 退出(例如通过 kill、System.exit())

如果应用程序被外部killSystem.exit()调用强制终止,则流是否正确关闭无关紧要。此外,在这两种情况下,都不能保证 finally 子句将被执行。

3) 硬件故障(或 JVM 中的错误以获取完整列表 :)

所有的赌注都取消了。您无法知道是否会执行任何操作,更不用说 finally 块了。

还有另一种情况,此时线程可能会收到一个自发的异常,并有一些(天真的)期望它可能会恢复。那是一些被误导的程序员决定调用已弃用的Thread.stop()方法的时候。您可能认为将流构造函数调用放在try块中会有所帮助。但实际上不会,因为在打开底层文件和完成流对象的构造之间,流构造函数内部ThreadDeath可能会引发异常。所以FD无论如何都会泄漏。

这只是不推荐使用的原因之一Thread.stop()。不要使用它。

于 2010-04-28T07:40:45.617 回答
5

a) 注意用interrupt() 中断线程不会立即生效,如果被中断的线程不合作,可能根本没有任何效果。在执行期间,线程不会因为中断()而退出:

Closeable in = new FileInputStream("data.txt");

唯一会发生的事情是它的线程中断标志将被打开。

b) 关于 OutOfMemoryError - 我看不出它是如何在构建输入流之后立即发生的。它可能发生在另一个线程中,但这不会立即对该线程产生影响。OutOfMemoryError 的问题是你的 finally 块也可能失败,因为没有足够的内存来完成它......

c) 我知道可以积极中断线程的唯一方法是使用已弃用的方法 Thread.stop() 和 Thread.stop(Throwable)。在此处查看类似的讨论: 这是在 Java 中释放资源的安全方法吗?

于 2010-04-28T07:18:13.823 回答
0

我的观点是,当您使用托管运行时,例如 Java 或 .NET 时,您真的不应该(这很好!)关注您的特定问题之类的事情。只是因为您与底层操作系统及其原生 API 完全断开了连接。你所需要知道的是你调用Closable.close()你的finally块,你的资源将永远被释放。

于 2010-04-28T07:04:33.373 回答