Java尝试最终的变化

Pet*_*ikh 5 java resources exception finally try-catch

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

Closeable in = new FileInputStream("data.txt");
try {
    doSomething(in);
} finally {
    in.close();
}
Run Code Online (Sandbox Code Playgroud)

和第二种变化

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

令我担心的部分是线程可能在获取资源的时刻(例如文件被打开)之间有些中断,但结果值未分配给相应的局部变量.是否还有其他情况,线程可能会在上述点之外被中断,除了:

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

我已经读到第二种方法有点"惯用",但IMO在上面的场景中没有区别,在所有其他场景中它们都是平等的.

所以问题是:

两者有什么不同?如果我担心释放资源(特别是在多线程应用程序中),我应该更喜欢哪个?为什么?

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

Ste*_*n C 6

我认为没有理由担心:

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

呼叫Thread.interrupt()不会导致InterruptedException自发抛出.该异常仅在特定(并且记录良好)的阻塞方法中抛出; 即阻塞I/O和同步方法.从流构造函数返回之后和进入try块之前,不能抛出此异常.

或抛出OutOfMemoryError异常

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

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

如果应用程序被外部killSystem.exit()呼叫强行终止,则流是否正确关闭并不重要.此外,在这两种情况下都无法保证最终条款的执行.

3)硬件失败(或JVM中的错误完整列表:)

所有赌注都已关闭.你无法知道是否会执行任何操作,更不用说finally块了.

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

这只是Thread.stop()弃用的一个原因.不要使用它.


Eya*_*der 5

a)注意,如果被中断的线程不合作,使用interrupt()中断线程将不会立即生效,并且可能根本没有任何影响.在执行以下期间,由于interrupt(),线程无法退出:

Closeable in = new FileInputStream("data.txt");
Run Code Online (Sandbox Code Playgroud)

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

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

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