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

2022-08-31 10:01:31

当我从 中阅读源代码时,我对它为什么写这样的代码感到困惑: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;
}

有人能给出合理的解释吗?


答案 1

如果你脱离上下文来看待这段代码,那么对于这个“别名”就没有很好的解释。它只是冗余代码或糟糕的代码样式。

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

线索是,在 中声明的是 。这意味着子类有可能进入并分配给 。鉴于这种可能性,“别名”实际上是为了防止争用条件。inFilterInputStreamprotected volatilenullin

考虑没有“别名”的代码

private InputStream getInIfOpen() throws IOException {
    if (in == null)
        throw new IOException("Stream closed");
    return in;
}
  1. 线程 A 调用getInIfOpen()
  2. 线程 A 计算并看到不是 。in == nullinnull
  3. 线程 B 分配给 。nullin
  4. 线程 A 执行 。返回的原因是 .return innullavolatile

“别名”可防止出现这种情况。现在由线程 A 只读取一次。如果线程 B 在线程 A 之后分配,则无关紧要。线程 A 将引发异常或返回(保证的)非空值。innullin


答案 2

这是因为该类是为多线程使用而设计的。BufferedInputStream

在这里,您可以看到 的声明,它被放置在父类中:inFilterInputStream

protected volatile InputStream in;

因为它是 ,它的值可以被 的任何子类改变,包括 和 它的子类。此外,它被声明为 ,这意味着如果任何线程更改了变量的值,则此更改将立即反映在所有其他线程中。这种组合很糟糕,因为这意味着该类无法控制或知道何时更改。因此,该值甚至可以在 中的 null 检查和 return 语句之间更改,这有效地使 null 检查变得毫无用处。通过只读取一次的值以将其缓存在局部变量中,该方法是安全的,以防止来自其他线程的更改,因为局部变量始终由单个线程拥有。protectedFilterInputStreamBufferedInputStreamvolatileBufferedInputStreaminBufferedInputStream::getInIfOpenininputBufferedInputStream::getInIfOpen

中有一个示例,它设置为 null:BufferedInputStream::closein

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::closeBufferedInputStream::getInIfOpen


推荐