为什么BufferedInputStream复制一个字段到本地variables而不是直接使用字段

当我从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直接使用字段variables:

 /** * 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是一个可以被子类化的类,它需要在multithreading环境下工作。

线索是在FilterInputStream in声明的是被protected volatile 。 这意味着有一个子类可以到达和分配null机会。 鉴于这种可能性,“别名”实际上是为了防止竞争状况。

考虑没有“别名”的代码

 private InputStream getInIfOpen() throws IOException { if (in == null) throw new IOException("Stream closed"); return in; } 
  1. 线程A调用getInIfOpen()
  2. 线程A in == null计算in == null并且看到in不是null
  3. 线程B将null指定给in
  4. 线程A执行return in 。 由于a是一个volatile所以返回null

“别名”阻止了这一点。 现在in线程A中只读一次。如果线程A在线程A之后赋值为null in则无关紧要。 线程A将会抛出一个exception或者返回一个(保证的)非空值。

这是因为类BufferedInputStream是为multithreading使用而devise的。

在这里,你看到了放在父类FilterInputStreamin的声明:

 protected volatile InputStream in; 

由于它是protected ,因此可以通过FilterInputStream的任何子类(包括BufferedInputStream及其子类)来更改其值。 此外,它被声明为volatile ,这意味着如果任何线程更改了variables的值,则此更改将立即反映在所有其他线程中。 这种组合是不好的,因为这意味着类BufferedInputStream无法控制或知道什么时候改变。 因此,在BufferedInputStream::getInIfOpen检查null和return语句之间的值甚至可以被改变,这有效地使null检查无效。 通过只读取一次的值将其caching在局部variablesinputBufferedInputStream::getInIfOpen方法可以安全地防止来自其他线程的更改,因为局部variables总是由单个线程拥有。

BufferedInputStream::close有一个例子,它设置为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::getInIfOpen由另一个线程调用BufferedInputStream::getInIfOpen ,则会导致上述竞争条件。

这是一个简短的代码,但理论上在multithreading环境下,比较之后可能会改变,所以这个方法可以返回一些没有检查的东西(它可以返回null ,从而做到这一点旨在防止)。

我相信捕获到本地variablesinput类variables是为了防止不一致的行为,如果in getInIfOpen()正在运行时由另一个线程更改。

请注意, in的所有者是父类,不会将其标记为final

这种模式在class级的其他部分被复制,似乎是合理的防御性编码。