Saat saya membaca source code dari java.io.BufferedInputStream.getInIfOpen()
, saya bingung kenapa menulis kode seperti ini:
/**
* 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;
}
Mengapa menggunakan alias daripada menggunakan variabel bidang in
secara langsung seperti di bawah ini:
/**
* 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;
}
Bisakah seseorang memberikan penjelasan yang masuk akal?
java
bufferedinputstream
Santo
sumber
sumber
Eclipse
, Anda tidak dapat menjeda debugger padaif
pernyataan. Mungkin menjadi alasan variabel alias itu. Hanya ingin membuangnya ke sana. Saya berspekulasi, tentu saja.if
pernyataan?Jawaban:
Jika Anda melihat kode ini di luar konteks, tidak ada penjelasan yang baik untuk "alias" itu. Ini hanyalah kode yang berlebihan atau gaya kode yang buruk.
Tapi konteksnya adalah itu
BufferedInputStream
adalah kelas yang bisa disubkelas, dan itu perlu bekerja dalam konteks multi-utas.Petunjuknya adalah yang
in
dideklarasikan dalamFilterInputStream
adalahprotected volatile
. Itu berarti bahwa ada kemungkinan bahwa subclass bisa mencapai dan menetapkannull
untukin
. Mengingat kemungkinan itu, "alias" sebenarnya ada untuk mencegah kondisi balapan.Pertimbangkan kode tanpa "alias"
getInIfOpen()
in == null
dan melihat yangin
tidaknull
.null
kein
.return in
. Yang mengembalikannull
karenaa
avolatile
."Alias" mencegah ini. Sekarang
in
dibaca hanya sekali oleh utas A. Jika utas B diberikannull
setelah utas A memilikinya,in
itu tidak masalah. Thread A akan memunculkan pengecualian atau mengembalikan nilai bukan null (dijamin).sumber
protected
variabel itu jahat dalam konteks multi-threaded.protected
variabel dalam kode kita jika itu multi-threaded?Ini karena kelas
BufferedInputStream
dirancang untuk penggunaan multi-utas.Di sini, Anda melihat deklarasi
in
, yang ditempatkan di kelas indukFilterInputStream
:Karena itu
protected
, nilainya dapat diubah oleh subkelas apa punFilterInputStream
, termasukBufferedInputStream
dan subkelasnya. Juga, itu dideklarasikanvolatile
, yang berarti bahwa jika ada thread yang mengubah nilai variabel, perubahan ini akan segera terlihat di semua thread lainnya. Kombinasi ini buruk, karena itu berarti kelasBufferedInputStream
tidak memiliki cara untuk mengontrol atau mengetahui kapanin
diubah. Dengan demikian, nilainya bahkan dapat diubah antara pemeriksaan null dan pernyataan return inBufferedInputStream::getInIfOpen
, yang secara efektif membuat pemeriksaan null tidak berguna. Dengan membaca nilaiin
hanya sekali untuk menyimpannya dalam cache di variabel lokalinput
, metodeBufferedInputStream::getInIfOpen
ini aman dari perubahan dari utas lain, karena variabel lokal selalu dimiliki oleh satu utas.Ada contoh di
BufferedInputStream::close
, yang disetelin
ke nol:Jika
BufferedInputStream::close
dipanggil oleh utas lain saatBufferedInputStream::getInIfOpen
dijalankan, ini akan menghasilkan kondisi balapan yang dijelaskan di atas.sumber
compareAndSet()
,CAS
, dll dalam kode dan di komentar. Saya juga mencariBufferedInputStream
kode dan menemukan banyaksynchronized
metode. Jadi, ini ditujukan untuk penggunaan multi-threaded, meski saya yakin belum pernah menggunakannya seperti itu. Bagaimanapun, saya pikir jawaban Anda benar!getInIfOpen()
hanya dipanggil daripublic synchronized
metodeBufferedInputStream
.Ini adalah kode yang singkat, tetapi, secara teoritis, dalam lingkungan multi-utas,
in
dapat berubah tepat setelah perbandingan, sehingga metode dapat mengembalikan sesuatu yang tidak diperiksa (dapat kembalinull
, sehingga melakukan hal yang tepat seperti yang dimaksudkan untuk mencegah).sumber
in
mungkin berubah antara waktu Anda memanggil metode dan mengembalikan nilai (dalam lingkungan multi-utas)?in
dapat berubah kapan saja).Saya percaya menangkap variabel kelas
in
ke variabel lokalinput
adalah untuk mencegah perilaku yang tidak konsisten jikain
diubah oleh utas lain saatgetInIfOpen()
sedang berjalan.Perhatikan bahwa pemilik
in
adalah kelas induk dan tidak menandainya sebagaifinal
.Pola ini direplikasi di bagian lain kelas dan tampaknya merupakan pengkodean pertahanan yang wajar.
sumber