这个问题困扰了我一段时间,但我还没有找到完整的答案(例如,这个问题是针对 C# Initializing disposable resources outside or inside 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();
}
让我担心的部分是,在获取资源(例如打开文件)之间线程可能会有些中断,但结果值不会分配给相应的局部变量。除以下情况外,是否还有其他情况线程可能会在上述情况下中断:
- 抛出 InterruptedException(例如通过 Thread#interrupt())或 OutOfMemoryError 异常
- JVM 退出(例如通过 kill、System.exit())
- 硬件故障(或 JVM 中的错误以获得完整列表:)
我读到第二种方法更“惯用”,但 IMO 在上面的场景中没有区别,在所有其他场景中它们是相同的。
所以问题:
两者有什么区别?如果我确实关心释放资源(尤其是在大量多线程应用程序中),我应该更喜欢哪个?为什么?
如果有人指出支持答案的部分 Java/JVM 规范,我将不胜感激。
最佳答案
我认为没有任何理由担心:
1) InterruptedException (e.g. via Thread#interrupt())
调用 Thread.interrupt()
不会导致自发抛出 InterruptedException
。异常仅在特定的(且有据可查的)阻塞方法中抛出;即阻塞 I/O 和同步方法。从流构造函数返回后,进入try block 之前,不能抛出此异常。
or OutOfMemoryError exception is thrown
如果抛出 OutOfMemoryError
,无论将流构造函数放在何处,都不能保证底层文件描述符的完全恢复。您永远不应该尝试从 OOM 中恢复,因此流是否关闭的问题没有实际意义。此外,此异常仅在实际尝试分配内存的线程上抛出,此时并没有发生。
2) JVM exits (e.g. via kill, System.exit())
如果应用程序被外部 kill
或 System.exit()
调用强制终止,则流是否正确关闭并不重要。此外,在这两种情况下,都不能保证 finally 子句会被执行。
3) Hardware fail (or bug in JVM for complete list :)
一切皆有可能。您无法知道是否有任何内容会执行,更不用说 finally block 了。
还有一种情况,线程可能会在此时收到一个自发的异常,并且有一些(天真的)期望它可能会恢复。那是一些误入歧途的程序员决定调用已弃用的 Thread.stop()
方法的时候。您可能认为将流构造函数调用放在 try
block 中会有所帮助。但实际上它不会,因为 ThreadDeath
异常可能会在打开基础文件和完成流对象的构造之间在流构造函数中引发。所以 FD 无论如何都可能泄漏。
这只是 Thread.stop()
被弃用的原因之一。不要使用它。
关于Java try finally 变体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2727353/