当我阅读java.io.BufferedInputStream.getInIfOpen()
的源代码时,我很困惑为什么它会写成这样的代码:
/**
* Check to make sure that underlying input stream has not been
* nulled out due to close; if not return it;
*/
private InputStream getInIfOpen() throws IOException {
InputStream input = in;
if (input == null)
throw new IOException("Stream closed");
return input;
}
为什么使用别名而不是直接使用字段变量in
,如下所示:
/**
* Check to make sure that underlying input stream has not been
* nulled out due to close; if not return it;
*/
private InputStream getInIfOpen() throws IOException {
if (in == null)
throw new IOException("Stream closed");
return in;
}
有人能给出一个合理的解释吗?
发布于 2016-03-26 10:59:26
这是因为BufferedInputStream
类是为多线程使用而设计的。
在这里,您可以看到放置在父类FilterInputStream
中的in
声明
protected volatile InputStream in;
因为它是protected
,所以它的值可以由FilterInputStream
的任何子类更改,包括BufferedInputStream
及其子类。此外,它被声明为volatile
,这意味着如果任何线程更改了变量的值,此更改将立即反映在所有其他线程中。这种组合是不好的,因为它意味着类BufferedInputStream
无法控制或知道in
何时发生更改。因此,甚至可以在检查null和BufferedInputStream::getInIfOpen
中的return语句之间更改该值,这实际上使得对null的检查变得无用。通过只读取一次in
的值以将其缓存在局部变量input
中,方法BufferedInputStream::getInIfOpen
不会受到其他线程的更改,因为局部变量始终归单个线程所有。
在BufferedInputStream::close
中有一个示例,它将in
设置为null:
public void close() throws IOException {
byte[] buffer;
while ( (buffer = buf) != null) {
if (bufUpdater.compareAndSet(this, buffer, null)) {
InputStream input = in;
in = null;
if (input != null)
input.close();
return;
}
// Else retry in case a new buf was CASed in fill()
}
}
如果在执行BufferedInputStream::getInIfOpen
时,另一个线程调用了BufferedInputStream::close
,则会导致上述争用情况。
发布于 2016-03-26 10:59:38
这是一段很短的代码,但从理论上讲,在多线程环境中,in
可能会在比较后立即更改,因此该方法可能会返回未检查的内容(它可能返回null
,从而做它想要阻止的事情)。
发布于 2016-03-26 11:02:03
我相信,将类变量in
捕获为局部变量input
可以防止在getInIfOpen()
运行时另一个线程更改in
时出现不一致的行为。
请注意,in
的所有者是父类,并且没有将其标记为final
。
这种模式在类的其他部分中也被复制,似乎是合理的防御性编码。
https://stackoverflow.com/questions/36231047
复制相似问题