Variabel yang mudah menguap memberikan visibilitas benang dan tidak menjamin keamanan dan atomisitas utas.
Apa itu visibilitas utas:
Kunci menyediakan dua fitur utama: pengecualian dan visibilitas timbal balik. Eksklusi timbal balik berarti bahwa hanya satu utas yang dapat menampung kunci spesifik pada satu waktu, sehingga fitur ini dapat digunakan untuk mengimplementasikan protokol akses terkoordinasi untuk data bersama, sehingga hanya satu utas yang dapat menggunakan data bersama pada satu waktu. Visibilitas sedikit lebih kompleks, dan harus memastikan bahwa perubahan pada data bersama sebelum melepaskan kunci terlihat oleh utas lain yang kemudian memperoleh kunci - tanpa jaminan visibilitas ini yang disediakan oleh mekanisme sinkronisasi, variabel bersama yang dilihat oleh utas dapat dimodifikasi atau nilai yang tidak konsisten, yang akan menyebabkan banyak masalah serius.
Lihat semantik volatile :
Volatile setara dengan implementasi yang lemah dari disinkronkan, yang berarti bahwa volatil mengimplementasikan semantik yang disinkronkan, tetapi tidak memiliki mekanisme kunci. Ini memastikan bahwa pembaruan ke bidang yang mudah menguap menginformasikan utas lain dengan cara yang dapat diprediksi.
Volatile berisi semantik berikut:
(1) Model penyimpanan Java tidak akan memesan ulang operasi instruksi valatile: Ini memastikan bahwa operasi pada variabel volatile dieksekusi dalam urutan di mana instruksi muncul.
(2) Variabel volatil tidak akan di -cache dalam register (hanya utas yang terlihat) atau tempat lain yang tidak terlihat oleh CPU. Hasil variabel volatil selalu dibaca dari memori utama setiap saat. Dengan kata lain, untuk modifikasi variabel volatil, utas lain selalu terlihat, dan mereka tidak menggunakan variabel di dalam tumpukan utas. Yaitu, dalam hukum yang terjadi sebelum, setelah menulis variabel valatile, operasi baca selanjutnya dapat dipahami untuk melihat hasil dari operasi penulisan ini.
Meskipun karakteristik variabel volatile baik, volatile tidak dapat menjamin keamanan utas. Dengan kata lain, pengoperasian bidang yang mudah menguap bukanlah atom. Variabel volatil hanya dapat menjamin visibilitas (utas lain dapat memahami hasilnya setelah melihat perubahan ini setelah satu utas dimodifikasi). Untuk memastikan atomisitas, Anda hanya bisa menguncinya sejauh ini!
Prinsip untuk menggunakan volatile:
Tiga prinsip untuk menerapkan variabel volatile:
(1) Tulis variabel tidak tergantung pada nilai variabel ini, atau hanya satu utas memodifikasi variabel ini
(2) Keadaan variabel tidak perlu berpartisipasi dalam kendala invarian dengan variabel lain
(3) Variabel akses tidak perlu dikunci
Faktanya, kondisi ini menunjukkan bahwa nilai -nilai yang valid yang dapat ditulis ke variabel yang mudah menguap tidak tergantung pada keadaan program apa pun, termasuk keadaan saat ini dari variabel.
Batas kondisi pertama mencegah variabel volatile digunakan sebagai penghitung yang aman. Meskipun operasi tambahan (X ++) terlihat seperti operasi yang terpisah, itu sebenarnya adalah operasi gabungan yang terdiri dari urutan operasi writ-write baca-modify yang harus dilakukan secara atom, dan volatil tidak dapat memberikan sifat atom yang diperlukan. Menerapkan operasi yang benar membutuhkan menjaga nilai konstan x selama operasi, yang tidak dimungkinkan dengan variabel volatil. (Namun, jika nilainya disesuaikan untuk ditulis hanya dari satu utas, kondisi pertama dapat diabaikan.)
Sebagian besar situasi pemrograman bertentangan dengan salah satu dari tiga kondisi ini, membuat variabel yang mudah menguap tidak berlaku secara universal untuk keselamatan utas sebagaimana disinkronkan. Listing 1 menunjukkan kelas rentang numerik yang tidak aman-aman. Ini berisi invarian - batas bawah selalu kurang dari atau sama dengan batas atas.
Gunakan volatile dengan benar:
Mode #1: Status Bendera
Mungkin spesifikasi penerapan variabel volatil adalah hanya menggunakan bendera status boolean untuk menunjukkan bahwa peristiwa satu kali penting telah terjadi, seperti menyelesaikan inisialisasi atau meminta downtime.
Banyak aplikasi termasuk struktur kontrol dalam bentuk "Eksekusi beberapa pekerjaan ketika program tidak siap untuk berhenti", seperti yang ditunjukkan dalam Listing 2:
Listing 2. Gunakan variabel volatil sebagai bendera status
shutdown Boolean yang mudah menguap memerlukan waktu; ... public void shutdown () {shutdownRestErest = true; } public void doWork () {while (! shutdown -recute) {// do stuff}}Sangat mungkin bahwa metode shutdown () akan dipanggil dari luar loop - yaitu di utas lain - sehingga beberapa jenis sinkronisasi perlu dilakukan untuk memastikan visibilitas variabel shutdown -fillquested diimplementasikan dengan benar. (Dapat dipanggil dari pendengar JMX, Operation Listener di utas acara GUI, melalui RMI, melalui layanan web, dll.). Namun, menulis loop dengan blok yang disinkronkan jauh lebih merepotkan daripada menulis dengan bendera status volatile yang ditunjukkan dalam Listing 2. Karena volatile menyederhanakan pengkodean dan bendera status tidak bergantung pada negara lain dalam program, sangat cocok untuk volatile di sini.
Fitur umum dari jenis tag keadaan ini adalah bahwa biasanya hanya ada satu transisi keadaan; Bendera shutdown -rayquest dikonversi dari false menjadi true, dan program berhenti. Pola ini dapat diperluas ke bendera status transisi bolak -balik, tetapi hanya dapat diperpanjang jika periode transisi tidak diperhatikan (dari palsu ke benar, kemudian menjadi salah). Selain itu, beberapa mekanisme konversi keadaan atom diperlukan, seperti variabel atom.
Mode #2: publikasi satu kali aman
Kurangnya sinkronisasi dapat menyebabkan visibilitas yang tidak dapat dicapai, yang membuatnya lebih sulit untuk menentukan kapan harus menulis referensi objek daripada nilai primitif. Dengan tidak adanya sinkronisasi, nilai yang diperbarui yang dirujuk oleh suatu objek (ditulis oleh utas lain) dapat ditemui dan nilai lama dari keadaan objek itu ada secara bersamaan. (Ini adalah akar penyebab dari masalah penguncian ganda yang terkenal di mana referensi objek dibaca tanpa sinkronisasi, menghasilkan masalah yang mungkin Anda lihat referensi yang diperbarui, tetapi masih melihat objek yang dibangun secara tidak lengkap melalui referensi itu).
Salah satu teknik untuk mengimplementasikan penerbitan objek yang aman adalah mendefinisikan referensi objek sebagai tipe yang mudah menguap. Listing 3 menunjukkan contoh di mana latar belakang utas memuat beberapa data dari database selama startup. Kode lain, ketika mereka dapat memanfaatkan data ini, periksa apakah itu telah dipublikasikan sebelum digunakan.
Listing 3. Menggunakan variabel volatil untuk rilis aman satu kali
latar belakang publicfloHleloader {public volatile floochle the flooBle; public void initinbackground () {// lakukan banyak hal theflooBle = new flooBle (); // Ini adalah satu -satunya menulis untuk theflooBle}} kelas publik someotherclass {public void dowork () {while (true) {// lakukan beberapa hal ... // gunakan flooBle, tetapi hanya jika siap jika (flooBleloader.thefloLoBle! = null) dosomething (flooBloader.thefloader.thefloLOBle); }}}Jika referensi flookle bukan tipe yang mudah menguap, kode di dowork () akan mendapatkan flooble yang dibangun secara tidak lengkap ketika tidak ada rujukannya.
Kondisi yang diperlukan untuk pola ini adalah bahwa objek yang diterbitkan harus aman-utas, atau objek abadi yang valid (efektif yang tidak dapat diubah berarti bahwa keadaan objek tidak akan pernah dimodifikasi setelah publikasi). Referensi tipe volatile memastikan visibilitas formulir publikasi objek, tetapi sinkronisasi tambahan diperlukan jika keadaan objek akan berubah setelah publikasi.
Pola #3: Pengamatan Independen
Mode sederhana lainnya menggunakan volatile dengan aman adalah untuk "melepaskan" pengamatan secara teratur untuk penggunaan internal program. Sebagai contoh, misalkan ada sensor sekitar yang mampu merasakan suhu sekitar. Utas latar belakang dapat membaca sensor setiap beberapa detik dan memperbarui variabel volatil yang berisi dokumen saat ini. Kemudian, utas lain dapat membaca variabel ini sehingga mereka dapat melihat nilai suhu terbaru kapan saja.
Aplikasi lain yang menggunakan mode ini adalah mengumpulkan statistik program. Listing 4 menunjukkan bagaimana mekanisme otentikasi mengingat nama pengguna yang masuk untuk terakhir kalinya. Ulangi referensi lastUser untuk menerbitkan nilai untuk bagian lain dari program ini.
Listing 4. Menggunakan variabel volatil untuk menerbitkan beberapa pengamatan independen
Public Class UserManager {Public Volatile String LastUser; public boolean authenticate (string user, string password) {boolean valid = passwordisValid (pengguna, kata sandi); if (valid) {user u = pengguna baru (); ActiveUsers.Add (U); lastuser = pengguna; } return valid; }}Mode ini adalah ekstensi dari mode sebelumnya; Menerbitkan nilai tertentu untuk digunakan di tempat lain dalam program ini, tetapi tidak seperti menerbitkan acara satu kali, itu adalah serangkaian acara independen. Pola ini mensyaratkan bahwa nilai yang diterbitkan adalah valid dan tidak berubah - yaitu, status nilai tidak akan berubah setelah publikasi. Kode yang menggunakan nilai ini harus jelas bahwa nilai dapat berubah kapan saja.
Mode #4: Mode "Volatile Bean"
Pola kacang yang mudah menguap cocok untuk kerangka kerja yang menggunakan Javabeans sebagai "struktur kehormatan". Dalam pola kacang yang mudah menguap, JavaBeans digunakan sebagai satu set wadah dengan sifat independen dari metode pengambil dan/atau setter. Prinsip dasar dari pola kacang yang mudah menguap adalah bahwa banyak kerangka kerja menyediakan wadah untuk pemegang data yang mudah menguap (seperti httpsession), tetapi objek yang ditempatkan dalam wadah ini harus aman.
Dalam mode kacang yang mudah menguap, semua anggota data Javabean adalah tipe yang mudah menguap, dan metode pengambil dan setter harus sangat biasa - mereka tidak dapat berisi logika apa pun kecuali untuk mendapatkan atau mengatur properti yang sesuai. Selain itu, untuk anggota data yang dirujuk oleh objek, objek yang direferensikan harus valid dan tidak berubah. (Ini akan melarang sifat dengan nilai array, karena ketika referensi array dinyatakan sebagai volatile, hanya referensi dan bukan array itu sendiri memiliki semantik yang mudah menguap). Untuk setiap variabel yang mudah menguap, invarian atau kendala tidak dapat berisi properti Javabean. Contoh -contoh dalam Listing 5 menunjukkan JavaBeans yang mematuhi pola kacang yang mudah menguap:
Mode #4: Mode "Volatile Bean"
@Threadsafe Public Class Person {Private Volatile String FirstName; nama terakhir string volatile pribadi; Usia int volatile pribadi; Public String getFirstName () {return firstName; } public String getLastName () {return lastName; } public int getage () {usia kembali; } public void setFirstName (String firstName) {this.firstName = firstName; } public void setLastName (String LastName) {this.LastName = LastName; } public void setage (int usia) {this.age = usia; }}Mode Lanjutan Volatile
Pola yang dijelaskan pada bagian sebelumnya mencakup sebagian besar kasus penggunaan dasar, dan menggunakan volatil dalam pola ini sangat berguna dan sederhana. Bagian ini memperkenalkan mode yang lebih maju di mana volatile akan memberikan keunggulan kinerja atau skalabilitas.
Mode lanjutan dari aplikasi volatil sangat rapuh. Oleh karena itu, asumsi harus dibuktikan dengan cermat, dan pola -pola ini dienkapsulasi secara ketat karena bahkan perubahan yang sangat kecil dapat merusak kode Anda! Demikian pula, alasan untuk menggunakan kasus penggunaan volatile yang lebih canggih adalah karena dapat meningkatkan kinerja, memastikan bahwa Anda benar -benar menentukan bahwa Anda perlu mencapai manfaat kinerja ini sebelum Anda mulai menerapkan pola canggih. Ada trade-off pada pola-pola ini, melepaskan keterbacaan atau pemeliharaan dengan imbalan kemungkinan perolehan kinerja-jika Anda tidak memerlukan peningkatan kinerja (atau tidak dapat membuktikan bahwa Anda membutuhkannya melalui program tes yang ketat), maka itu kemungkinan akan menjadi kesepakatan yang buruk karena Anda mungkin akan kehilangan lebih sedikit uang dan mendapatkan sesuatu yang lebih murah daripada apa yang Anda berikan.
Mode #5: Strategi kunci baca-tulis dengan overhead rendah
Sejauh ini, Anda harus memahami bahwa volatile tidak cukup mampu untuk mengimplementasikan penghitung. Karena ++ X sebenarnya merupakan kombinasi sederhana dari tiga operasi (baca, tambahkan, simpan), jika beberapa utas kebetulan mencoba melakukan operasi tambahan pada penghitung volatil pada saat yang sama, nilai yang diperbarui mungkin hilang.
Namun, jika operasi yang dibaca lebih dari sekadar operasi tulis, Anda dapat menggunakan kunci internal dan variabel volatil untuk mengurangi overhead jalur kode publik. Penghitung yang aman-utas yang ditunjukkan dalam Listing 6 menggunakan disinkronkan untuk memastikan bahwa operasi tambahan adalah atom dan fluktuatif untuk memastikan visibilitas hasil saat ini. Metode ini dapat mencapai kinerja yang lebih baik jika pembaruan tidak sering terjadi, karena overhead jalur baca hanya melibatkan operasi baca yang mudah menguap, yang biasanya lebih baik daripada overhead akuisisi kunci bebas kompetisi.
Listing 6. Gunakan volatil dan disinkronkan untuk mencapai "kunci baca overhead yang lebih rendah"
@Threadsafe kelas publik cheesycounter {// menggunakan trik kunci baca-write murah // semua operasi mutatif harus dilakukan dengan kunci 'ini' diadakan @guardedby ("this") nilai int volatile pribadi; public int getValue () {nilai pengembalian; } public sinchronized int increment () {return value ++; }}Alasan Teknik ini disebut "Kunci Baca-Baca Lower Head" adalah karena Anda menggunakan mekanisme sinkronisasi yang berbeda untuk operasi baca-tulis. Karena operasi tulis dalam contoh ini melanggar kondisi pertama menggunakan volatile, penghitung tidak dapat diimplementasikan dengan aman dengan volatile - Anda harus menggunakan kunci. Namun, Anda dapat menggunakan volatile dalam operasi baca untuk memastikan visibilitas nilai saat ini, sehingga Anda dapat menggunakan kunci untuk melakukan semua perubahan dan hanya membaca dengan volatile. Di antara mereka, kunci memungkinkan hanya satu utas untuk mengakses nilai pada satu waktu, dan volatile memungkinkan banyak utas untuk melakukan operasi baca. Oleh karena itu, saat menggunakan volatile untuk memastikan bahwa jalur kode dibaca, lebih banyak berbagi daripada menggunakan kunci untuk menjalankan semua jalur kode - seperti operasi baca -tulis. Namun, perlu diingat kelemahan model ini dalam pikiran: jika aplikasi paling mendasar dari model ini adalah di luar, akan menjadi sangat sulit untuk menggabungkan dua mekanisme sinkronisasi yang bersaing ini.
Tentang Instruksi Papan Pesan dan Aturan yang Terjadi Sebelum
1. Mari kita pesan ulang
Spesifikasi bahasa Java menetapkan bahwa utas JVM mempertahankan semantik berurutan secara internal, yaitu, selama hasil akhir dari program ini setara dengan hasilnya dalam lingkungan berurutan yang ketat, urutan eksekusi instruksi mungkin tidak konsisten dengan urutan kode. Proses ini disusun ulang oleh perintah. Pentingnya penyusutan ulang instruksi adalah bahwa JVM dapat memesan ulang instruksi mesin dengan tepat sesuai dengan karakteristik prosesor (sistem cache multi-level CPU, prosesor multi-core, dll.), Sehingga instruksi mesin lebih sesuai dengan karakteristik eksekusi CPU dan memaksimalkan kinerja mesin.
Model paling sederhana untuk eksekusi program adalah untuk mengeksekusi dalam urutan di mana instruksi muncul, yang tidak tergantung pada CPU yang mengeksekusi instruksi, memastikan portabilitas instruksi sampai tingkat terbesar. Istilah profesional untuk model ini disebut model konsistensi berurutan. Namun, sistem komputer modern dan arsitektur prosesor tidak menjamin hal ini (karena penunjukan buatan tidak selalu menjamin kepatuhan terhadap karakteristik pemrosesan CPU).
2. Aturan Appens-sebelum
Model penyimpanan Java memiliki prinsip yang terjadi sebelum, yaitu, jika tindakan B ingin melihat hasil eksekusi dari tindakan A (terlepas dari apakah A/B dieksekusi di utas yang sama), maka A/B perlu memenuhi hubungan yang terjadi sebelum.
Sebelum memperkenalkan aturan yang terjadi sebelum, perkenalkan konsep: JMM Action (Java Memeory Model Action), Java menyimpan tindakan model. Suatu tindakan meliputi: Variabel Baca dan Tulis, Kunci Penguncian dan Rilis Monitor, Start Thread () dan Join (). Kunci akan disebutkan nanti.
terjadi sebelum aturan lengkap:
(1) Setiap tindakan di utas yang sama terjadi sebelum tindakan apa pun yang muncul setelahnya.
(2) Membuka kunci monitor terjadi sebelum setiap kunci berikutnya pada monitor yang sama.
(3) Tulis operasi ke bidang volatil terjadi sebelum setiap operasi baca berikutnya dari bidang yang sama.
(4) Panggilan ke thread.start () akan terjadi sebelum tindakan di utas startup.
(5) Semua tindakan dalam utas terjadi sebelum pemeriksaan pada utas lain untuk mengakhiri utas ini atau dikembalikan di thread.join () atau thread.isalive () == false.
(6) Satu utas A memanggil interrupt () dari utas lain B terjadi sebelum ketika utas A menemukan bahwa B tergelincir oleh A (B melempar pengecualian atau A mendeteksi B terputus () atau terganggu ()).
(7) Akhir dari konstruktor objek terjadi sebelum dan awal finalisasi objek
(8) Jika suatu tindakan terjadi setelah tindakan B, dan tindakan B terjadi sebelumnya dan tindakan C, maka tindakan yang terjadi setelah tindakan C.
Di atas adalah semua tentang artikel ini. Saya akan memperkenalkannya kepada Anda di sini. Saya harap akan sangat membantu bagi Anda untuk belajar dan memahami variabel yang mudah menguap di Java.