When I read the source code from java.io.BufferedInputStream.getInIfOpen()
, I am confused about why it wrote code like this:
/**
* 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;
}
Why does it use the alias instead of use the field variable in
directly like below:
/**
* 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;
}
Can someone give a reasonable explanation?
If you look at this code out of context there is no good explanation for that "alias". It is simply redundant code or poor code style.
But the context is that BufferedInputStream
is a class that can be subclassed, and that it needs to work in a multi-threaded context.
The clue is that in
is declared in FilterInputStream
is protected volatile
. That means that there is a chance that a subclass could reach in and assign null
to in
. Given that possibility, the "alias" is actually there to prevent a race condition.
Consider the code without the "alias"
private InputStream getInIfOpen() throws IOException {
if (in == null)
throw new IOException("Stream closed");
return in;
}
getInIfOpen()
in == null
and sees that in
is not null
.null
to in
.return in
. Which returns null
because a
is a volatile
. The "alias" prevents this. Now in
is read just once by thread A. If thread B assigns null
after thread A has in
it doesn't matter. Thread A will either throw an exception or return a (guaranteed) non-null value.
This is because the class BufferedInputStream
is designed for multi-threaded usage.
Here, you see the declaration of in
, which is placed in the parent class FilterInputStream
:
protected volatile InputStream in;
Since it is protected
, its value can be changed by any subclass of FilterInputStream
, including BufferedInputStream
and its subclasses. Also, it is declared volatile
, which means that if any thread changes the value of the variable, this change will immediately be reflected in all other threads. This combination is bad, since it means the class BufferedInputStream
has no way to control or know when in
is changed. Thus, the value can even be changed between the check for null and the return statement in BufferedInputStream::getInIfOpen
, which effectively makes the check for null useless. By reading the value of in
only once to cache it in the local variable input
, the method BufferedInputStream::getInIfOpen
is safe against changes from other threads, since local variables are always owned by a single thread.
There is an example in BufferedInputStream::close
, which sets in
to 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()
}
}
If BufferedInputStream::close
is called by another thread while BufferedInputStream::getInIfOpen
is executed, this would result in the race condition described above.
这是一个如此短的代码,但是,理论上,在多线程环境中, in
可能会在比较后立即更改,因此该方法可能会返回它没有检查的内容(它可能会返回null
,从而执行它原来的操作)意在防止)。
I believe capturing the class variable in
to the local variable input
is to prevent inconsistent behavior if in
is change by another thread while getInIfOpen()
is running.
Notice that the owner of in
is the parent class and does not mark it as final
.
This pattern is replicated in other parts of the class and seems to be reasonable defensive coding.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.