Guru saya di kelas Java tingkat atas tentang threading mengatakan sesuatu yang saya tidak yakin.
Dia menyatakan bahwa kode berikut belum tentu memperbarui ready
variabel. Menurutnya, kedua utas tidak harus berbagi variabel statis, khususnya dalam kasus ketika setiap utas (utas utama versus ReaderThread
) berjalan pada prosesornya sendiri dan oleh karena itu tidak berbagi register / cache / dll yang sama dan satu CPU tidak akan memperbarui yang lain.
Pada dasarnya, dia mengatakan itu mungkin yang ready
diperbarui di utas utama, tetapi BUKAN di ReaderThread
, sehingga ReaderThread
akan berulang tanpa batas.
Dia juga mengklaim program itu mungkin untuk dicetak 0
atau 42
. Saya mengerti bagaimana 42
bisa dicetak, tapi tidak 0
. Dia menyebutkan ini akan menjadi kasus ketika number
variabel disetel ke nilai default.
Saya pikir mungkin tidak ada jaminan bahwa variabel statis diperbarui di antara utas, tetapi ini menurut saya sangat aneh untuk Java. Apakah membuat ready
volatile memperbaiki masalah ini?
Dia menunjukkan kode ini:
public class NoVisibility {
private static boolean ready;
private static int number;
private static class ReaderThread extends Thread {
public void run() {
while (!ready) Thread.yield();
System.out.println(number);
}
}
public static void main(String[] args) {
new ReaderThread().start();
number = 42;
ready = true;
}
}
sumber
Jawaban:
Tidak ada yang istimewa tentang variabel statis dalam hal visibilitas. Jika mereka dapat diakses, utas apa pun bisa mendapatkannya, jadi Anda lebih cenderung melihat masalah konkurensi karena mereka lebih terbuka.
Ada masalah visibilitas yang diberlakukan oleh model memori JVM. Berikut adalah artikel yang membahas tentang model memori dan bagaimana penulisan dapat dilihat oleh utas . Anda tidak dapat mengandalkan perubahan yang dibuat satu utas agar terlihat oleh utas lain secara tepat waktu (sebenarnya JVM tidak berkewajiban untuk membuat perubahan itu terlihat oleh Anda sama sekali, dalam kerangka waktu apa pun), kecuali Anda membuat terjadi sebelum .
Berikut kutipan dari tautan itu (disediakan dalam komentar oleh Jed Wesley-Smith):
sumber
Dia berbicara tentang visibilitas dan tidak untuk dipahami terlalu harfiah.
Variabel statis memang dibagi di antara utas, tetapi perubahan yang dibuat di satu utas mungkin tidak langsung terlihat ke utas lain, membuatnya tampak seperti ada dua salinan variabel.
Artikel ini menyajikan pandangan yang sejalan dengan cara dia menyajikan info:
Tapi sekali lagi, ini hanyalah model mental untuk berpikir tentang threading dan volatile, tidak secara harfiah bagaimana JVM bekerja.
sumber
Pada dasarnya memang benar, tetapi sebenarnya masalahnya lebih kompleks. Visibilitas data bersama dapat dipengaruhi tidak hanya oleh cache CPU, tetapi juga oleh eksekusi instruksi yang tidak teratur.
Oleh karena itu, Java mendefinisikan Model Memori , yang menyatakan dalam situasi apa thread dapat melihat status konsisten dari data bersama.
Dalam kasus khusus Anda, menambahkan
volatile
jaminan visibilitas.sumber
Mereka tentu saja "dibagi" dalam arti bahwa keduanya merujuk ke variabel yang sama, tetapi mereka tidak selalu melihat pembaruan satu sama lain. Ini berlaku untuk variabel apa pun, tidak hanya statis.
Dan secara teori, penulisan yang dibuat oleh thread lain dapat tampak dalam urutan yang berbeda, kecuali jika variabel dideklarasikan
volatile
atau penulisan disinkronkan secara eksplisit.sumber
Dalam satu classloader, kolom statis selalu dibagikan. Untuk secara eksplisit mencakup data ke utas, Anda ingin menggunakan fasilitas seperti
ThreadLocal
.sumber
Saat Anda menginisialisasi variabel tipe primitif statis, java default memberikan nilai untuk variabel statis
ketika Anda mendefinisikan variabel seperti ini nilai default i = 0; itulah mengapa ada kemungkinan untuk mendapatkan Anda 0. maka utas utama memperbarui nilai boolean siap menjadi true. karena ready adalah variabel statis, utas utama, dan utas lainnya merujuk ke alamat memori yang sama sehingga variabel siap berubah. sehingga benang sekunder keluar dari loop sementara dan mencetak nilai. saat mencetak nilai nilai inisialisasi angka adalah 0. jika proses utas telah berlalu sementara loop sebelum utas utama memperbarui variabel angka. maka ada kemungkinan untuk mencetak 0
sumber
@dontocsata Anda dapat kembali ke guru Anda dan menyekolahkannya sedikit :)
sedikit catatan dari dunia nyata dan apa pun yang Anda lihat atau ceritakan. Harap DICATAT, kata-kata di bawah ini berkaitan dengan kasus khusus ini dalam urutan persis seperti yang ditunjukkan.
2 variabel berikut akan berada di baris cache yang sama di bawah hampir semua arsitektur yang diketahui.
Thread.exit
(utas utama) dijamin keluar danexit
dijamin menyebabkan pagar memori, karena penghapusan utas grup utas (dan banyak masalah lainnya). (ini adalah panggilan tersinkronisasi, dan saya tidak melihat cara tunggal untuk diimplementasikan tanpa bagian sinkronisasi karena ThreadGroup harus diakhiri juga jika tidak ada untaian daemon yang tersisa, dll).Utas yang dimulai
ReaderThread
akan menjaga proses tetap hidup karena ini bukan daemon! Jadiready
dannumber
akan disiram bersama-sama (atau nomor sebelumnya jika sakelar konteks terjadi) dan tidak ada alasan nyata untuk mengurutkan ulang dalam kasus ini setidaknya saya bahkan tidak dapat memikirkannya. Anda akan membutuhkan sesuatu yang benar-benar aneh untuk melihat apa pun kecuali42
. Sekali lagi saya menganggap kedua variabel statis akan berada di baris cache yang sama. Saya tidak bisa membayangkan panjang baris cache 4 byte ATAU JVM yang tidak akan menempatkan mereka di area berkelanjutan (baris cache).sumber