Quando leio o código-fonte do java.io.BufferedInputStream.getInIfOpen()
, fico confuso sobre por que ele escreveu um 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 que está usando o alias em vez de usar a variável de campo in
diretamente como abaixo:
/**
* 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;
}
Alguém pode dar uma explicação razoável?
java
bufferedinputstream
Santo
fonte
fonte
Eclipse
, você não pode pausar um depurador em umaif
instrução. Pode ser um motivo para essa variável de alias. Só queria jogar isso lá fora. Eu especulo, é claro.if
declaração?Respostas:
Se você olhar para este código fora do contexto, não há uma boa explicação para esse "alias". É simplesmente código redundante ou estilo de código pobre.
Mas o contexto é que
BufferedInputStream
é uma classe que pode ser subclassificada e que precisa funcionar em um contexto multi-thread.A pista é que
in
está declarado emFilterInputStream
isprotected volatile
. Isso significa que há uma chance de que uma subclasse possa alcançar e atribuirnull
ain
. Dada essa possibilidade, o "alias" está realmente lá para evitar uma condição de corrida.Considere o código sem o "alias"
getInIfOpen()
in == null
e vê quein
não énull
.null
ain
.return in
. Que retornanull
porquea
é avolatile
.O "alias" impede isso. Agora
in
é lido apenas uma vez pelo encadeamento A. Se o encadeamento B for atribuídonull
após o encadeamento A terin
, não importa. O thread A lançará uma exceção ou retornará um valor não nulo (garantido).fonte
protected
variáveis são ruins em um contexto multithread.protected
variáveis em nosso código se ele for multi-threaded?Isso ocorre porque a classe
BufferedInputStream
foi projetada para uso multi-thread.Aqui, você vê a declaração de
in
, que é colocada na classe paiFilterInputStream
:Visto que é
protected
, seu valor pode ser alterado por qualquer subclasse deFilterInputStream
, includingBufferedInputStream
e suas subclasses. Além disso, é declaradovolatile
, o que significa que se qualquer segmento alterar o valor da variável, essa alteração será imediatamente refletida em todos os outros segmentos. Essa combinação é ruim, pois significa que a classeBufferedInputStream
não tem como controlar ou saber quandoin
é alterada. Assim, o valor pode até ser alterado entre a verificação de nulo e a instrução de retorno emBufferedInputStream::getInIfOpen
, o que efetivamente torna a verificação de nulo inútil. Ao ler o valor dein
apenas uma vez para armazená-lo em cache na variável localinput
, o métodoBufferedInputStream::getInIfOpen
é seguro contra alterações de outras threads, uma vez que as variáveis locais sempre pertencem a uma única thread.Há um exemplo em
BufferedInputStream::close
, que definein
como nulo:Se
BufferedInputStream::close
for chamado por outro encadeamento enquantoBufferedInputStream::getInIfOpen
é executado, isso resultaria na condição de corrida descrita acima.fonte
compareAndSet()
,CAS
, etc. no código e nos comentários. Também pesquisei oBufferedInputStream
código e encontrei váriossynchronized
métodos. Portanto, ele se destina ao uso multi-threaded, embora eu nunca tenha usado dessa forma. Enfim, acho que sua resposta está correta!getInIfOpen()
só é chamado depublic synchronized
métodos deBufferedInputStream
.Este é um código tão curto, mas, teoricamente, em um ambiente multi-threaded,
in
pode mudar logo após a comparação, então o método pode retornar algo que não foi verificado (ele pode retornarnull
, fazendo exatamente o que foi feito para evita).fonte
in
pode mudar entre o momento em que você chama o método e o retorno do valor (em um ambiente multi-thread)?in
pode mudar a qualquer momento).Acredito que capturar a variável de classe
in
para a variável localinput
é evitar um comportamento inconsistente sein
for alterado por outro segmento durante agetInIfOpen()
execução.Observe que o proprietário de
in
é a classe pai e não a marca comofinal
.Esse padrão é replicado em outras partes da classe e parece ser uma codificação defensiva razoável.
fonte