Pertanyaan Apakah variabel statis dibagi antar utas?


Guru saya di kelas java tingkat atas pada threading mengatakan sesuatu yang saya tidak yakin.

Dia menyatakan bahwa kode berikut tidak akan selalu memperbarui ready variabel. Menurutnya, dua utas tidak selalu berbagi variabel statis, khususnya dalam kasus ketika setiap utas (utas utama versus PustakaThread) berjalan pada prosesornya sendiri dan oleh karena itu tidak berbagi register / cache / etc yang sama dan satu CPU tidak akan memperbarui yang lain.

Pada dasarnya, dia mengatakan itu mungkin ready diperbarui di utas utama, tetapi TIDAK di PustakaThread, sehingga ReaderThread akan berputar tanpa batas. Dia juga mengklaim itu mungkin untuk program untuk mencetak '0' atau '42'. Saya mengerti bagaimana '42' bisa dicetak, tetapi tidak '0'. Dia menyebutkan ini akan menjadi kasus ketika number variabel diatur ke nilai default.

Saya pikir mungkin itu tidak menjamin bahwa variabel statis diperbarui antara utas, tetapi ini menurut saya sangat aneh untuk Java. Apakah membuat ready menguap benar masalah ini?

Dia menunjukkan kode ini:

NoVisibility kelas publik {
    boolean statis swasta siap;
    nomor int statis pribadi;
    private static class ReaderThread memperluas Thread {
        public void run () {
            while (! ready) Thread.yield ();
            System.out.println (angka);
        }
    }
    public static void main (String [] args) {
        new ReaderThread (). start ();
        angka = 42;
        siap = benar;
    }
}

75
2018-02-08 15:25


asal


Jawaban:


Tidak ada masalah visibilitas khusus untuk variabel statis. Ada masalah visibilitas yang dikenakan oleh model memori JVM. Berikut ini artikel yang membahas tentang model memori dan bagaimana penulisan menjadi terlihat oleh utas. Anda tidak dapat menghitung perubahan satu utas yang membuat terlihat oleh utas lainnya secara tepat waktu (sebenarnya JVM tidak memiliki kewajiban untuk membuat perubahan itu terlihat bagi Anda sama sekali), kecuali jika Anda membuat terjadi sebelum hubungan, inilah kutipan dari tautan itu (disediakan dalam komentar oleh Jed Wesley-Smith):

Bab 17 dari Spesifikasi Bahasa Jawa mendefinisikan hubungan yang terjadi sebelum pada operasi memori seperti membaca dan menulis variabel yang dipakai bersama. Hasil dari penulisan oleh satu utas dijamin akan terlihat untuk dibaca oleh utas lain hanya jika operasi tulis terjadi sebelum operasi baca. Konstruk tersinkronisasi dan mudah menguap, serta metode Thread.start () dan Thread.join (), dapat membentuk hubungan yang terjadi sebelum. Khususnya:

  • Setiap tindakan dalam utas terjadi sebelum setiap tindakan dalam utas yang datang kemudian di urutan program.

  • Buka kunci (blok tersinkronisasi atau keluar metode) monitor terjadi sebelum setiap kunci berikutnya (blok disinkronkan atau entri metode) dari monitor yang sama. Dan karena relasi yang terjadi sebelumnya adalah transitif, semua tindakan utas sebelum membuka kunci terjadi sebelum semua tindakan setelah penguncian thread apa pun yang dipantau.

  • Tulis ke bidang yang mudah menguap terjadi sebelum setiap pembacaan berikutnya dari bidang yang sama. Menulis dan membaca bidang volatil memiliki efek konsistensi memori yang sama seperti masuk dan keluar dari monitor, tetapi tidak memerlukan penguncian saling pengecualian.

  • Panggilan untuk memulai pada utas terjadi sebelum tindakan apa pun di utas dimulai.

  • Semua tindakan dalam untaian terjadi sebelum untaian lain berhasil kembali dari gabung pada utas tersebut.


57
2018-02-08 15:31



Dia sedang berbicara tentang visibilitas dan tidak dianggap terlalu harfiah.

Variabel statis memang dibagi di antara untaian, tetapi perubahan yang dibuat dalam satu utas mungkin tidak langsung terlihat di utas lain, membuatnya tampak seperti ada dua salinan variabel.

Artikel ini menyajikan tampilan yang konsisten dengan cara dia menyajikan info:

Pertama, Anda harus memahami sedikit sesuatu tentang model memori Java. Saya telah berjuang sedikit selama bertahun-tahun untuk menjelaskannya secara singkat dan baik. Mulai hari ini, cara terbaik yang bisa saya pikirkan untuk menggambarkannya adalah jika Anda membayangkannya seperti ini:

  • Setiap thread di Java terjadi di ruang memori yang terpisah (ini jelas tidak benar, jadi bersabarlah dengan yang satu ini).

  • Anda perlu menggunakan mekanisme khusus untuk menjamin bahwa komunikasi terjadi di antara untaian ini, seperti yang Anda lakukan pada sistem pengiriman pesan.

  • Memori menulis yang terjadi dalam satu utas dapat "bocor" dan dilihat oleh utas lain, tetapi ini tidak berarti dijamin. Tanpa komunikasi eksplisit, Anda tidak dapat menjamin tulisan mana yang dapat dilihat oleh utas lainnya, atau bahkan urutan di mana mereka terlihat.

...

thread model

Tetapi sekali lagi, ini hanyalah model mental untuk berpikir tentang threading dan volatile, tidak secara harfiah bagaimana JVM bekerja.


28
2018-02-08 15:35



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 sesuai pesanan.

Oleh karena itu Java mendefinisikan a Model Memori, yang menyatakan dalam keadaan mana utas dapat melihat keadaan konsisten dari data yang dibagikan.

Dalam kasus khusus Anda, tambahkan volatile menjamin visibilitas.


9
2018-02-08 15:33



Mereka "berbagi" tentu saja dalam arti bahwa mereka berdua mengacu pada variabel yang sama, tetapi mereka tidak selalu melihat pembaruan masing-masing. Ini berlaku untuk variabel apa pun, tidak hanya statis.

Dan dalam teori, penulisan yang dibuat oleh utas lain dapat muncul dalam urutan yang berbeda, kecuali variabel dinyatakan volatile atau tulisan disinkronkan secara eksplisit.


5
2018-02-08 15:34



Dalam satu kelas tunggal, bidang statis selalu dibagikan. Untuk secara eksplisit menjangkau data ke utas, Anda ingin menggunakan fasilitas seperti ThreadLocal.


4
2018-02-08 15:27



Ketika Anda menginisialisasi variabel java tipe primitif statis default memberikan nilai untuk variabel statis

public static int i ;

ketika Anda mendefinisikan variabel seperti ini nilai default dari i = 0; Itulah mengapa ada kemungkinan untuk mendapatkan Anda 0. kemudian utas utama memperbarui nilai boolean ready to true. karena siap adalah variabel statis, utas utama dan referensi thread lainnya ke alamat memori yang sama sehingga perubahan variabel siap. jadi utas sekunder keluar dari while loop dan print value. saat mencetak nilai nilai yang diinisialisasi dari angka adalah 0. jika proses thread telah berlalu sementara loop sebelum variabel nomor pembaruan utas utama. maka ada kemungkinan untuk mencetak 0


1
2018-04-19 14:31



@dontocsata Anda dapat kembali ke guru dan sekolah Anda sedikit :)

beberapa catatan dari dunia nyata dan tidak peduli apa yang Anda lihat atau diberi tahu. Harap dicatat, kata-kata di bawah ini berkaitan dengan kasus khusus ini dalam urutan yang tepat ditunjukkan.

Variabel 2 berikut akan berada pada baris cache yang sama di bawah hampir semua arsitektur tahu.

private static boolean ready;  
private static int number;  

Thread.exit (utas utama) dijamin untuk keluar dan exit dijamin menyebabkan pagar memori, karena penghapusan thread thread group (dan banyak masalah lainnya). (Ini adalah panggilan yang disinkronkan, dan saya tidak melihat satu cara untuk diimplementasikan tanpa bagian sync karena ThreadGroup harus berhenti juga jika tidak ada benang daemon yang tersisa, dll).

Benang mulai ReaderThread akan menjaga proses tetap hidup karena ini bukan daemon! Demikian ready dan number akan di-flush bersama-sama (atau nomor sebelumnya jika konteks beralih terjadi) dan tidak ada alasan nyata untuk penataan kembali dalam kasus ini setidaknya saya tidak dapat memikirkannya. Anda akan membutuhkan sesuatu yang benar-benar aneh untuk melihat apa pun kecuali 42. Sekali lagi saya menganggap kedua variabel statis akan berada di baris cache yang sama. Saya tidak bisa membayangkan cache line 4 byte panjang ATAU JVM yang tidak akan menetapkan mereka di daerah yang terus menerus (cache line).


-2
2018-02-11 00:06