Cuando leo el código fuente de java.io.BufferedInputStream.getInIfOpen()
, estoy confundido acerca de por qué escribió un código como este:
/**
* 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;
}
¿Por qué usa el alias en lugar de usar la variable de campo in
directamente como se muestra a continuación?
/**
* 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;
}
¿Alguien puede dar una explicación razonable?
java
bufferedinputstream
Santo
fuente
fuente
Eclipse
, no puede pausar un depurador en unaif
declaración. Podría ser una razón para esa variable de alias. Solo quería tirar eso por ahí. Especulo, por supuesto.if
declaración?Respuestas:
Si observa este código fuera de contexto, no hay una buena explicación para ese "alias". Es simplemente código redundante o estilo de código deficiente.
Pero el contexto es que
BufferedInputStream
es una clase que puede subclasificarse y que necesita funcionar en un contexto de subprocesos múltiples.La pista es que
in
se declara enFilterInputStream
isprotected volatile
. Eso significa que hay una posibilidad de que una subclase puede meter la mano y asignarnull
ain
. Dada esa posibilidad, el "alias" está realmente ahí para evitar una condición de carrera.Considere el código sin el "alias"
getInIfOpen()
in == null
y ve quein
no lo esnull
.null
ain
.return in
. Que devuelvenull
porquea
es unvolatile
.El "alias" evita esto. Ahora
in
se lee solo una vez por el hilo A. Si el hilo B se asignanull
después del hilo Ain
, no importa. El hilo A lanzará una excepción o devolverá un valor no nulo (garantizado).fuente
protected
variables son malas en un contexto de subprocesos múltiples.protected
variables en nuestro código si es multiproceso?Esto se debe a que la clase
BufferedInputStream
está diseñada para uso de subprocesos múltiples.Aquí, ve la declaración de
in
, que se coloca en la clase principalFilterInputStream
:Dado que lo es
protected
, su valor puede ser cambiado por cualquier subclase deFilterInputStream
, incluyendoBufferedInputStream
y sus subclases. Además, se declaravolatile
, lo que significa que si algún hilo cambia el valor de la variable, este cambio se reflejará inmediatamente en todos los demás hilos. Esta combinación es mala, ya que significa que la claseBufferedInputStream
no tiene forma de controlar o saber cuándoin
se cambia. Por lo tanto, el valor incluso se puede cambiar entre la verificación de nulo y la declaración de retorno enBufferedInputStream::getInIfOpen
, lo que efectivamente hace que la verificación de nulo sea inútil. Al leer el valor dein
solo una vez para almacenarlo en caché en la variable localinput
, el métodoBufferedInputStream::getInIfOpen
es seguro contra cambios de otros subprocesos, ya que las variables locales siempre son propiedad de un solo subproceso.Hay un ejemplo en
BufferedInputStream::close
, que se establecein
en nulo:Si
BufferedInputStream::close
es llamado por otro hilo mientrasBufferedInputStream::getInIfOpen
se ejecuta, esto daría como resultado la condición de carrera descrita anteriormente.fuente
compareAndSet()
,CAS
, etc., en el código y en los comentarios. También busqué elBufferedInputStream
código y encontré numerosossynchronized
métodos. Por lo tanto, está diseñado para un uso de subprocesos múltiples, aunque seguro que nunca lo he usado de esa manera. De todos modos, ¡creo que tu respuesta es correcta!getInIfOpen()
solo se llama desdepublic synchronized
métodos deBufferedInputStream
.Este es un código tan corto, pero, teóricamente, en un entorno de subprocesos múltiples,
in
puede cambiar justo después de la comparación, por lo que el método podría devolver algo que no verificó (podría regresarnull
, haciendo así exactamente lo que estaba destinado a hacer evitar).fuente
in
podría cambiar entre el momento en que llama al método y la devolución del valor (en un entorno de subprocesos múltiples)?in
puede cambiar en cualquier momento).Creo que capturar la variable de clase
in
en la variable localinput
es para evitar un comportamiento inconsistente siin
otro subproceso lo cambia mientras segetInIfOpen()
está ejecutando.Tenga en cuenta que el propietario de
in
es la clase principal y no la marca comofinal
.Este patrón se replica en otras partes de la clase y parece ser una codificación defensiva razonable.
fuente