为什么BufferedInputStream将一个字段复制到局部变量而不是直接使用该字段
Why does BufferedInputStream copy a field to a local variable rather than use the field directly
当我阅读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;
}
谁能给个合理的解释?
如果你断章取意地看这段代码,就没有很好的解释 "alias"。这只是冗余代码或糟糕的代码风格。
但是上下文是BufferedInputStream
是一个class可以subclassed,需要在多线程上下文中工作。
线索是in
声明在FilterInputStream
是protected volatile
。这意味着子 class 有可能进入并将 null
分配给 in
。鉴于这种可能性,"alias" 实际上是为了防止竞争条件。
考虑没有 "alias"
的代码
private InputStream getInIfOpen() throws IOException {
if (in == null)
throw new IOException("Stream closed");
return in;
}
- 线程 A 调用
getInIfOpen()
- 线程 A 计算
in == null
并发现 in
不是 null
。
- 线程 B 将
null
分配给 in
。
- 线程 A 执行
return in
。其中 returns null
因为 a
是 volatile
.
"alias" 可以防止这种情况。现在 in
只被线程 A 读取一次。如果线程 B 在线程 A 具有 in
之后分配 null
则没有关系。线程 A 将抛出异常或 return 一个(保证的)非空值。
这是因为 class BufferedInputStream
是为多线程使用而设计的。
在这里,你看到in
的声明,它被放置在父class FilterInputStream
:
protected volatile InputStream in;
因为它是protected
,它的值可以被FilterInputStream
的任何子class改变,包括BufferedInputStream
和它的子class。此外,它被声明为 volatile
,这意味着如果任何线程更改变量的值,此更改将立即反映在所有其他线程中。这种组合很糟糕,因为这意味着 class BufferedInputStream
无法控制或知道 in
何时更改。因此,甚至可以在检查 null 和 BufferedInputStream::getInIfOpen
中的 return 语句之间更改该值,这实际上使检查 null 变得无用。通过只读取一次 in
的值并将其缓存在局部变量 input
中,方法 BufferedInputStream::getInIfOpen
可以安全地防止来自其他线程的更改,因为局部变量始终由单个线程拥有.
BufferedInputStream::close
中有一个示例,它将in
设置为空:
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::close
在执行 BufferedInputStream::getInIfOpen
时被另一个线程调用,这将导致上述竞争条件。
这是一个很短的代码,但是,理论上,在多线程环境中,in
可能会在比较后立即发生变化,因此该方法可能 return 它没有检查的东西(它可以 return null
,从而完成它本来要防止的事情)。
我相信将 class 变量 in
捕获到局部变量 input
是为了防止在 in
被另一个线程更改而 getInIfOpen()
时出现不一致的行为] 是 运行.
请注意 in
的所有者是父级 class,并且没有将其标记为 final
。
此模式在 class 的其他部分复制并且似乎是合理的防御性编码。
当我阅读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;
}
谁能给个合理的解释?
如果你断章取意地看这段代码,就没有很好的解释 "alias"。这只是冗余代码或糟糕的代码风格。
但是上下文是BufferedInputStream
是一个class可以subclassed,需要在多线程上下文中工作。
线索是in
声明在FilterInputStream
是protected volatile
。这意味着子 class 有可能进入并将 null
分配给 in
。鉴于这种可能性,"alias" 实际上是为了防止竞争条件。
考虑没有 "alias"
的代码private InputStream getInIfOpen() throws IOException {
if (in == null)
throw new IOException("Stream closed");
return in;
}
- 线程 A 调用
getInIfOpen()
- 线程 A 计算
in == null
并发现in
不是null
。 - 线程 B 将
null
分配给in
。 - 线程 A 执行
return in
。其中 returnsnull
因为a
是volatile
.
"alias" 可以防止这种情况。现在 in
只被线程 A 读取一次。如果线程 B 在线程 A 具有 in
之后分配 null
则没有关系。线程 A 将抛出异常或 return 一个(保证的)非空值。
这是因为 class BufferedInputStream
是为多线程使用而设计的。
在这里,你看到in
的声明,它被放置在父class FilterInputStream
:
protected volatile InputStream in;
因为它是protected
,它的值可以被FilterInputStream
的任何子class改变,包括BufferedInputStream
和它的子class。此外,它被声明为 volatile
,这意味着如果任何线程更改变量的值,此更改将立即反映在所有其他线程中。这种组合很糟糕,因为这意味着 class BufferedInputStream
无法控制或知道 in
何时更改。因此,甚至可以在检查 null 和 BufferedInputStream::getInIfOpen
中的 return 语句之间更改该值,这实际上使检查 null 变得无用。通过只读取一次 in
的值并将其缓存在局部变量 input
中,方法 BufferedInputStream::getInIfOpen
可以安全地防止来自其他线程的更改,因为局部变量始终由单个线程拥有.
BufferedInputStream::close
中有一个示例,它将in
设置为空:
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::close
在执行 BufferedInputStream::getInIfOpen
时被另一个线程调用,这将导致上述竞争条件。
这是一个很短的代码,但是,理论上,在多线程环境中,in
可能会在比较后立即发生变化,因此该方法可能 return 它没有检查的东西(它可以 return null
,从而完成它本来要防止的事情)。
我相信将 class 变量 in
捕获到局部变量 input
是为了防止在 in
被另一个线程更改而 getInIfOpen()
时出现不一致的行为] 是 运行.
请注意 in
的所有者是父级 class,并且没有将其标记为 final
。
此模式在 class 的其他部分复制并且似乎是合理的防御性编码。