Когда я читаю исходный код 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
напрямую, как показано ниже:
/**
* 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;
}
Может кто-нибудь дать разумное объяснение?
java
bufferedinputstream
Святой
источник
источник
Eclipse
, вы не можете приостановить отладчик дляif
оператора. Может быть причиной этой переменной псевдонима. Просто хотел выбросить это там. Я, конечно, размышляю.if
заявлении?Ответы:
Если вы посмотрите на этот код вне контекста, для этого «псевдонима» нет хорошего объяснения. Это просто избыточный код или плохой стиль кода.
Но контекст таков, что
BufferedInputStream
это класс, который может быть разделен на подклассы, и что он должен работать в многопоточном контексте.Подсказка в том, что
in
заявлено вFilterInputStream
isprotected volatile
. Это означает, что есть шанс, что подкласс может проникнуть внутрь и назначитьnull
егоin
. Учитывая такую возможность, "псевдоним" на самом деле существует для предотвращения состояния гонки.Рассмотрим код без "псевдонима"
getInIfOpen()
in == null
и видит, чтоin
это не такnull
.null
кin
.return in
. Что возвращается,null
потому чтоa
это файлvolatile
.«Псевдоним» предотвращает это. Теперь поток A
in
читает только один раз. Если поток B назначаетсяnull
после потока A,in
это не имеет значения. Поток A либо вызовет исключение, либо вернет (гарантированное) ненулевое значение.источник
protected
переменные - зло в многопоточном контексте.protected
переменные в нашем коде, если он многопоточный?Это потому, что класс
BufferedInputStream
предназначен для многопоточного использования.Здесь вы видите объявление
in
, которое помещено в родительский классFilterInputStream
:Поскольку это так
protected
, его значение может быть изменено любым подклассомFilterInputStream
, включаяBufferedInputStream
и его подклассы. Кроме того, он объявленvolatile
, что означает, что если какой-либо поток изменит значение переменной, это изменение немедленно отразится во всех других потоках. Эта комбинация плохая, поскольку она означает, что классBufferedInputStream
не имеет возможности контролировать или знать, когдаin
изменяется. Таким образом, значение можно даже изменить между проверкой на null и оператором return inBufferedInputStream::getInIfOpen
, что фактически делает проверку на null бесполезной. Считывая значениеin
только один раз, чтобы кэшировать его в локальной переменнойinput
, методBufferedInputStream::getInIfOpen
защищен от изменений из других потоков, поскольку локальные переменные всегда принадлежат одному потоку.Вот пример, в
BufferedInputStream::close
котором установленоin
значение null:Если
BufferedInputStream::close
вызывается другим потоком во времяBufferedInputStream::getInIfOpen
выполнения, это приведет к состоянию гонки, описанному выше.источник
compareAndSet()
,CAS
и т.д. в коде и в комментариях. Я также поискалBufferedInputStream
код и нашел множествоsynchronized
методов. Итак, он предназначен для многопоточного использования, хотя я никогда не использовал его таким образом. Во всяком случае, я думаю, что ваш ответ правильный!getInIfOpen()
вызывается только изpublic synchronized
методовBufferedInputStream
.Это такой короткий код, но теоретически в многопоточной среде он
in
может измениться сразу после сравнения, поэтому метод может возвращать то, что он не проверял (он мог возвращатьnull
, тем самым делая то, для чего он был предназначен. предотвращать).источник
in
может измениться между моментом вызова метода и возвратом значения (в многопоточной среде)?in
может измениться в любой момент).Я считаю, что запись переменной класса
in
в локальную переменнуюinput
предназначена для предотвращения несогласованного поведения, еслиin
она изменяется другим потоком во времяgetInIfOpen()
работы.Обратите внимание, что владельцем
in
является родительский класс, и он не помечается какfinal
.Этот образец воспроизводится в других частях класса и кажется разумным защитным кодированием.
источник