Sai*_*int 107 java bufferedinputstream
当我从中读取源代码时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;
}
Run Code Online (Sandbox Code Playgroud)
为什么它使用别名而不是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;
}
Run Code Online (Sandbox Code Playgroud)
有人可以给出合理的解释吗?
Ste*_*n C 119
如果你看看这个代码脱离了上下文,那么"别名"没有很好的解释.它只是冗余代码或糟糕的代码风格.
但是上下文是BufferedInputStream一个可以被子类化的类,并且它需要在多线程上下文中工作.
线索是,in被宣布FilterInputStream为protected volatile.这意味着子类可能会进入并分配null给in.鉴于这种可能性,"别名"实际上是为了防止竞争条件.
考虑没有"别名"的代码
private InputStream getInIfOpen() throws IOException {
if (in == null)
throw new IOException("Stream closed");
return in;
}
Run Code Online (Sandbox Code Playgroud)
getInIfOpen()in == null并看到in不是null.null给in.return in.哪个返回null因为a是volatile."别名"阻止了这一点.现在in只用线程A读取一次.如果线程B null在线程A之后分配in则无关紧要.线程A将抛出异常或返回(保证)非空值.
Ste*_*ase 20
这是因为该类BufferedInputStream是为多线程使用而设计的.
在这里,您可以看到声明in,它放在父类中FilterInputStream:
protected volatile InputStream in;
Run Code Online (Sandbox Code Playgroud)
既然如此protected,它的值可以被FilterInputStream包括BufferedInputStream它的子类和子类的任何子类改变.此外,它被声明volatile,这意味着如果任何线程更改变量的值,此更改将立即反映在所有其他线程中.这种组合很糟糕,因为这意味着课程BufferedInputStream无法控制或知道何时in更改.因此,甚至可以在检查null和return语句之间更改该值BufferedInputStream::getInIfOpen,这有效地使得检查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()
}
}
Run Code Online (Sandbox Code Playgroud)
如果BufferedInputStream::close在BufferedInputStream::getInIfOpen执行时由另一个线程调用,则这将导致上述竞争条件.