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 indirectamente 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 unaifdeclaración. Podría ser una razón para esa variable de alias. Solo quería tirar eso por ahí. Especulo, por supuesto.ifdeclaració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
BufferedInputStreames una clase que puede subclasificarse y que necesita funcionar en un contexto de subprocesos múltiples.La pista es que
inse declara enFilterInputStreamisprotected volatile. Eso significa que hay una posibilidad de que una subclase puede meter la mano y asignarnullain. Dada esa posibilidad, el "alias" está realmente ahí para evitar una condición de carrera.Considere el código sin el "alias"
getInIfOpen()in == nully ve queinno lo esnull.nullain.return in. Que devuelvenullporqueaes unvolatile.El "alias" evita esto. Ahora
inse lee solo una vez por el hilo A. Si el hilo B se asignanulldespués del hilo Ain, no importa. El hilo A lanzará una excepción o devolverá un valor no nulo (garantizado).fuente
protectedvariables son malas en un contexto de subprocesos múltiples.protectedvariables en nuestro código si es multiproceso?Esto se debe a que la clase
BufferedInputStreamestá 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, incluyendoBufferedInputStreamy 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 claseBufferedInputStreamno tiene forma de controlar o saber cuándoinse 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 deinsolo una vez para almacenarlo en caché en la variable localinput, el métodoBufferedInputStream::getInIfOpenes 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 estableceinen nulo:Si
BufferedInputStream::closees llamado por otro hilo mientrasBufferedInputStream::getInIfOpense 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é elBufferedInputStreamcódigo y encontré numerosossynchronizedmé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 synchronizedmétodos deBufferedInputStream.Este es un código tan corto, pero, teóricamente, en un entorno de subprocesos múltiples,
inpuede 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
inpodría cambiar entre el momento en que llama al método y la devolución del valor (en un entorno de subprocesos múltiples)?inpuede cambiar en cualquier momento).Creo que capturar la variable de clase
inen la variable localinputes para evitar un comportamiento inconsistente siinotro subproceso lo cambia mientras segetInIfOpen()está ejecutando.Tenga en cuenta que el propietario de
ines 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