问题
当我从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;
}
为什么它使用别名而不是直接使用字段variablein
,如下所示:
/**
* 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;
}
有人可以给出合理的解释吗?
#1 热门回答(119 赞)
如果你在上下文中看这个代码,那么"别名"没有很好的解释。它只是冗余代码或糟糕的代码风格。
但是上下文是BufferedInputStream
是一个可以被子类化的类,并且它需要在多线程上下文中工作。
线索是thein
在FilterInputStream
isprotected volatile
中声明。这意味着子类有可能进入并且分配了null
到in
。鉴于这种可能性,"别名"实际上是为了防止竞争条件。
考虑没有"别名"的代码
private InputStream getInIfOpen() throws IOException {
if (in == null)
throw new IOException("Stream closed");
return in;
}
-线程A调用getInIfOpen()
- 线程A在== null中求值,并看到in不为null。
- 线程B将null赋给。
- 线程A执行return in。返回null,因为a是volatile。
"别名"阻止了这一点。 Nowin
只通过线程A读取一次。如果线程B指定null
,则线程A具有in
it无关紧要。线程A将抛出异常或返回(保证)非空值。
#2 热门回答(20 赞)
这是因为classBufferedInputStream
是为多线程使用而设计的。
在这里,你可以看到声明in
,它位于父类FilterInputStream
中:
protected volatile InputStream in;
由于它是protected
,因此可以通过任何子类26565326更改其值,包括BufferedInputStream
及其子类。此外,它声明为volatile
,这意味着如果任何线程更改了变量的值,则此更改将立即反映在所有其他线程中。这种组合很糟糕,因为这意味着classBufferedInputStream
无法控制或知道in
何时被更改。因此,甚至可以在检查null和返回语句BufferedInputStream::getInIfOpen
之间更改该值,这有效地使得检查null无用。通过仅读取值in
以将其缓存在本地变量input
中,方法BufferedInputStream::getInIfOpen
可以安全地防止来自其他线程的更改,因为局部变量始终由单个线程拥有。
有一个例子inBufferedInputStream::close
,其中setsin
为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()
}
}
IfBufferedInputStream::close
由另一个线程调用而执行BufferedInputStream::getInIfOpen
时,这将导致上述竞争条件。
#3 热门回答(6 赞)
这是一个很短的代码,但理论上,在多线程环境中,in
可能会在比较后立即更改,因此该方法可以返回它未检查的内容(它可以返回8962679721,因此执行它的意思是避免)。