当我从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;
}
有人可以给出合理的解释吗?
答案 0 :(得分:119)
如果从上下文中查看此代码,则对“别名”没有很好的解释。它只是冗余的代码或糟糕的代码风格。
但是上下文是in
是一个可以被子类化的类,并且它需要在多线程上下文中工作。
线索是FilterInputStream
中声明的protected volatile
是null
。这意味着子类可能会进入并将in
分配给private InputStream getInIfOpen() throws IOException {
if (in == null)
throw new IOException("Stream closed");
return in;
}
。鉴于这种可能性,“别名”实际上是为了防止竞争条件。
考虑没有“别名”的代码
getInIfOpen()
in == null
in
并发现null
不是null
。in
分配给return in
。null
。这会返回a
,因为volatile
是in
。“别名”阻止了这一点。现在null
只被线程A读取一次。如果线程B在线程A有in
之后分配$conn
则无关紧要。线程A将抛出异常或返回(保证)非空值。
答案 1 :(得分:20)
这是因为类BufferedInputStream
是为多线程使用而设计的。
在这里,您会看到in
的声明,该声明位于父类FilterInputStream
中:
protected volatile InputStream in;
由于它是protected
,因此FilterInputStream
的任何子类都可以更改其值,包括BufferedInputStream
及其子类。此外,它声明为volatile
,这意味着如果任何线程更改了变量的值,则此更改将立即反映在所有其他线程中。这种组合很糟糕,因为这意味着类BufferedInputStream
无法控制或知道何时更改in
。因此,甚至可以在BufferedInputStream::getInIfOpen
中检查null和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::close
时另一个线程调用BufferedInputStream::getInIfOpen
,则会导致上述竞争条件。
答案 2 :(得分:6)
这是一个很短的代码,但理论上,在多线程环境中,in
可能会在比较后立即更改,因此该方法可以返回它没有检查的内容(它可以返回null
,因此做了它本来要防止的确切事情。)
答案 3 :(得分:4)
我认为将类变量in
捕获到局部变量input
是为了防止in
在getInIfOpen()
运行时由另一个线程更改in
时出现不一致的行为。
请注意,final
的所有者是父类,并未将其标记为import React, { Component } from 'react'
import { render } from 'react-dom'
export default class aPage extends Component {
render() {
return (
<div>
<div className="kittensExample">
<img className="kittens" src = 'https://kittensonsomewebpage.jpg' />
</div>
</div>
)
}
}
。
这种模式在课堂的其他部分被复制,似乎是合理的防御性编码。