为什么BufferedInputStream将字段复制到局部变量而不是直接使用该字段

2020年2月8日 14点热度 0条评论

当我从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;
}

有人可以给出合理的解释吗?

解决方案如下:

如果您是从上下文中看这段代码,那么对于“别名”就没有很好的解释。它仅仅是冗余代码或不良的代码样式。

但是上下文是BufferedInputStream是可以子类化的类,并且它需要在多线程上下文中工作。

线索是in中声明的FilterInputStreamprotected volatile。这意味着子类有可能进入并为null分配in。考虑到这种可能性,实际上会使用“别名”来防止出现竞争状况。

考虑不带“别名”的代码

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。由于nulla,因此返回volatile
  • “别名”可以防止这种情况。现在
    in只能由线程A读取一次。如果线程B在线程A具有
    null之后分配了
    in,则没有关系。线程A将抛出异常或返回(保证的)非空值。