Kiedy czytam kod źródłowy java.io.BufferedInputStream.getInIfOpen()
, nie rozumiem, dlaczego napisał on taki kod:
/**
* 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;
}
Dlaczego używa aliasu zamiast używać zmiennej pola in
bezpośrednio, jak poniżej:
/**
* 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;
}
Czy ktoś może podać rozsądne wyjaśnienie?
java
bufferedinputstream
Święty
źródło
źródło
Eclipse
nie można wstrzymać debugera naif
instrukcji. Może być powodem tej zmiennej aliasu. Chciałem tylko to wyrzucić. Oczywiście spekuluję.if
wyciągu?Odpowiedzi:
Jeśli spojrzysz na ten kod z kontekstu, nie ma dobrego wyjaśnienia tego „aliasu”. Jest to po prostu nadmiarowy kod lub słaby styl kodu.
Ale kontekst jest taki, że
BufferedInputStream
jest to klasa, która może być podklasą i że musi działać w kontekście wielowątkowym.Wskazówka jest taka, że
in
zadeklarowano wFilterInputStream
isprotected volatile
. Oznacza to, że istnieje szansa, że podklasa może dotrzeć do i przypisaćnull
doin
. Biorąc pod uwagę taką możliwość, „alias” faktycznie istnieje, aby zapobiec sytuacji wyścigu.Rozważ kod bez „aliasu”
getInIfOpen()
in == null
i widzi, żein
tak nie jestnull
.null
doin
.return in
. Który powraca,null
ponieważa
jestvolatile
.„Alias” zapobiega temu. Teraz
in
jest czytany tylko raz przez wątek A. Jeśli wątek B przypisuje sięnull
po wątku Ain
, nie ma to znaczenia. Wątek A zgłosi wyjątek lub zwróci (gwarantowaną) wartość różną od null.źródło
protected
zmienne są złe w kontekście wielowątkowym.protected
zmiennych w naszym kodzie, jeśli jest on wielowątkowy?Dzieje się tak, ponieważ klasa
BufferedInputStream
jest przeznaczona do użytku wielowątkowego.Tutaj widzisz deklarację
in
, która jest umieszczona w klasie nadrzędnejFilterInputStream
:Ponieważ tak jest
protected
, jego wartość może zostać zmieniona przez dowolną podklasęFilterInputStream
, w tymBufferedInputStream
i jej podklasy. Ponadto jest zadeklarowanavolatile
, co oznacza, że jeśli którykolwiek wątek zmieni wartość zmiennej, ta zmiana zostanie natychmiast odzwierciedlona we wszystkich innych wątkach. Ta kombinacja jest zła, ponieważ oznacza, że klasaBufferedInputStream
nie może kontrolować ani wiedzieć, kiedyin
zostanie zmieniona. W związku z tym wartość można nawet zmienić między sprawdzeniem wartości null a instrukcją return inBufferedInputStream::getInIfOpen
, co skutecznie sprawia, że sprawdzanie wartości null jest bezużyteczne. Odczytując wartośćin
tylko raz w celu buforowania jej w zmiennej lokalnejinput
, metodaBufferedInputStream::getInIfOpen
jest zabezpieczona przed zmianami z innych wątków, ponieważ zmienne lokalne są zawsze własnością jednego wątku.Oto przykład w programie
BufferedInputStream::close
, który ustawiain
wartość null:Jeśli
BufferedInputStream::close
jest wywoływana przez inny wątek podczasBufferedInputStream::getInIfOpen
wykonywania, spowodowałoby to sytuację wyścigu opisaną powyżej.źródło
compareAndSet()
,CAS
itp w kodzie iw komentarzach. Przeszukałem równieżBufferedInputStream
kod i znalazłem wielesynchronized
metod. Jest więc przeznaczony do użytku wielowątkowego, chociaż z pewnością nigdy nie używałem go w ten sposób. W każdym razie myślę, że twoja odpowiedź jest prawidłowa!getInIfOpen()
jest wywoływane tylko zpublic synchronized
metodBufferedInputStream
.To jest taki krótki kod, ale teoretycznie w środowisku wielowątkowym
in
może się zmienić zaraz po porównaniu, więc metoda może zwrócić coś, czego nie sprawdzała (mogła zwrócićnull
, robiąc dokładnie to, do czego była przeznaczona zapobiec).źródło
in
może się zmienić między momentem wywołania metody a zwróceniem wartości (w środowisku wielowątkowym)?in
może się zmienić w dowolnym momencie).Uważam, że przechwycenie zmiennej klasy
in
do zmiennej lokalnejinput
ma zapobiec niespójnemu zachowaniu, jeśliin
zostanie zmieniony przez inny wątek podczasgetInIfOpen()
działania.Zwróć uwagę, że właścicielem
in
jest klasa nadrzędna i nie oznacza jej jakofinal
.Ten wzorzec jest powielany w innych częściach klasy i wydaje się być rozsądnym kodowaniem obronnym.
źródło