当前位置: 首页 > 知识库问答 >
问题:

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

袁桐
2023-03-14

当我从java阅读源代码时。伊奥。缓冲数据流。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;
}

为什么它使用别名,而不是直接在中使用字段变量,如下所示:

/**
 * 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;
}

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


共有3个答案

锺离辰沛
2023-03-14
匿名用户

这是一段很短的代码,但从理论上讲,在多线程环境中,中的可能会在比较后立即更改,因此该方法可能会返回它没有检查的内容(它可能会返回null,从而执行它想要防止的事情)。

郏正信
2023-03-14
匿名用户

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

在这里,您可以看到中的声明,它位于父类FilterInputStream中:

protected volatile InputStream in;

由于它是受保护的,它的值可以被FilterInputStream的任何子类更改,包括BufferedInputStream及其子类。此外,它被声明为易失性,这意味着如果任何线程更改了变量的值,则此更改将立即反映在所有其他线程中。这种组合是不好的,因为它意味着类BufferedInputStream无法控制或知道中的何时被更改。因此,该值甚至可以在检查null和返回语句之间更改BufferedInputStream::getInIfOpen,这实际上使检查null变得无用。通过只读取中的值一次,将其缓存在局部变量输入中,方法BufferedInputStream::getInIfOpen可以安全地抵抗来自其他线程的更改,因为局部变量总是由单线程。

BufferedInputStream::close中有一个例子,它将中的设置为空:

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::getInIfOpen时,另一个线程调用了BufferedInputStream::close,这将导致上述竞争条件。

颜畅
2023-03-14
匿名用户

如果你断章取义地看这段代码,对这个“别名”没有很好的解释。它只是冗余代码或糟糕的代码风格。

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

线索是in中声明的FilterInputStreamprotected。这意味着子类有可能进入并分配nullin。考虑到这种可能性,“别名”实际上是为了防止种族状况。

考虑没有“别名”的代码

private InputStream getInIfOpen() throws IOException {
    if (in == null)
        throw new IOException("Stream closed");
    return in;
}
  1. 线程A调用getInIfOpen()
  2. 线程A计算in==null并看到in不是null
  3. 线程B将null赋值到中的
  4. 线程A执行返回。它返回null,因为a不稳定的

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

 类似资料:
  • 问题内容: 当我从中读取源代码时,我很困惑为什么它编写了这样的代码: 为什么使用别名而不是直接使用field变量,如下所示: 有人可以给出合理的解释吗? 问题答案: 如果您是从上下文中看这段代码,那么对于“别名”将没有很好的解释。它仅仅是冗余代码或不良的代码样式。 但是上下文是可以被子类化的类,它需要在多线程上下文中工作。 线索是,在宣布IS 。这意味着子类有可能进入并分配给。考虑到这种可能性,实

  • 问题内容: 我正在阅读的源代码,并发现了一些我不理解的代码: 注意这一行: 为什么不直接使用它,而是将其分配给局部变量? 问题答案: 可能是出于 优化 目的吗? 可能可以通过JIT编译器更轻松地将局部变量直接分配给寄存器。 至少在Android中,对于API的第一个版本,访问局部变量比访问实例变量便宜(不能代表较新的版本)。普通的Java可能是相同的,在某些情况下使用本地是有意义的。 实际上,在这

  • 如果我在我的类中创建一个bool,就像一样,它默认为false。 当我在我的方法中创建相同的bool时,我得到一个错误“使用未分配的局部变量检查”。为什么?

  • :我知道如何: 运行不带参数的私有void方法 :我知道如何: 设置任意类型的私有字段 设置任意类型的私有静态字段 设置任意类型的私有最终字段 设置任意类型的私有静态最终字段 :我知道如何: 获取任何类型的私有字段 :我知道如何: (可用于以单模式创建私有构造函数的新实例,同时保持实例Field为空) 创建不带参数的私有构造函数的新实例 我不知道的,我想知道的: 获取任何类型的私有静态字段,并将其

  • 本地风险值背后的想法是最终的,而领域不是。

  • 我看到了这种奇怪的行为,我想知道是否有合理的解释: 当我(意外地)在函数的局部变量中添加了一个额外的分号时,如: 它确实可以编译,但它表明它是多余的。 但当我对字段执行此操作时(也是偶然),我得到了一个错误(编译): 问题 是否有任何理由限制这些领域? Nb我已经知道了不允许带有字段的另一个限制因素。但这里有所不同。