Dalam pemrosesan serentak thread Java, saat ini terdapat banyak kebingungan tentang penggunaan kata kunci volatil. Diperkirakan dengan menggunakan kata kunci ini, semuanya akan baik-baik saja saat melakukan pemrosesan serentak multi-thread.
Bahasa Java mendukung multi-threading. Untuk memecahkan masalah konkurensi thread, mekanisme blok tersinkronisasi dan kata kunci volatil diperkenalkan di dalam bahasa tersebut.
disinkronkan
Semua orang akrab dengan blok tersinkronisasi, yang diimplementasikan melalui kata kunci tersinkronisasi. Dengan penambahan pernyataan tersinkronisasi dan blok, ketika diakses oleh beberapa utas, hanya satu utas yang dapat menggunakan metode modifikasi tersinkronisasi atau blok kode pada saat yang bersamaan.
tidak stabil
Untuk variabel yang dimodifikasi dengan volatil, setiap kali thread menggunakan variabel tersebut, ia akan membaca nilai variabel yang dimodifikasi. Volatile dapat dengan mudah disalahgunakan untuk melakukan operasi atom.
Mari kita lihat contoh di bawah ini. Kita mengimplementasikan sebuah counter. Setiap kali thread dimulai, metode counter inc akan dipanggil untuk menambahkan satu ke counter.
Lingkungan eksekusi - versi jdk: jdk1.6.0_31, memori: 3G cpu: x86 2.4G
Copy kode kodenya sebagai berikut:
Penghitung kelas publik {
jumlah int statis publik = 0;
publik statis kekosongan inc() {
//Tunda 1 milidetik di sini untuk memperjelas hasilnya
mencoba {
Thread.tidur(1);
} tangkapan (InterruptedException e) {
}
hitungan++;
}
public static void main(String[] args) {
//Memulai 1000 thread secara bersamaan untuk melakukan perhitungan i++ dan melihat hasil sebenarnya
untuk (int saya = 0; saya < 1000; saya++) {
Utas baru(yang baru Dapat Dijalankan() {
@Mengesampingkan
menjalankan kekosongan publik() {
Counter.inc();
}
}).awal();
}
//Nilai di sini mungkin berbeda setiap kali dijalankan, mungkin 1000
System.out.println("Hasil dijalankan: Counter.count=" + Counter.count);
}
}
Hasil berjalan: Counter.count=995
Hasil operasi sebenarnya mungkin berbeda setiap saat. Hasil dari mesin ini adalah: hasil yang berjalan: Counter.count=995. Terlihat bahwa dalam lingkungan multi-thread, Counter.count tidak mengharapkan hasilnya menjadi 1000.
Banyak orang mengira ini adalah masalah konkurensi multi-thread. Anda hanya perlu menambahkan volatil sebelum variabel count untuk menghindari masalah ini. Kemudian kami akan memodifikasi kodenya untuk melihat apakah hasilnya sesuai dengan harapan kami.
Copy kode kodenya sebagai berikut:
Penghitung kelas publik {
hitungan int statis volatil publik = 0;
publik statis kekosongan inc() {
//Tunda 1 milidetik di sini untuk memperjelas hasilnya
mencoba {
Thread.tidur(1);
} tangkapan (InterruptedException e) {
}
hitungan++;
}
public static void main(String[] args) {
//Memulai 1000 thread secara bersamaan untuk melakukan perhitungan i++ dan melihat hasil sebenarnya
untuk (int saya = 0; saya < 1000; saya++) {
Utas baru(yang baru Dapat Dijalankan() {
@Mengesampingkan
menjalankan kekosongan publik() {
Counter.inc();
}
}).awal();
}
//Nilai di sini mungkin berbeda setiap kali dijalankan, mungkin 1000
System.out.println("Hasil dijalankan: Counter.count=" + Counter.count);
}
}
Hasil berjalan: Counter.count=992
Hasil runningnya masih belum 1000 seperti yang kita harapkan. Mari kita analisa alasannya di bawah ini.
Artikel Java Garbage Collection menjelaskan alokasi memori selama runtime JVM. Salah satu area memori adalah tumpukan mesin virtual jvm. Setiap thread memiliki tumpukan thread saat dijalankan. Ketika sebuah thread mengakses nilai suatu objek, pertama-tama ia menemukan nilai variabel yang sesuai dengan memori heap melalui referensi objek, dan kemudian memuat nilai spesifik dari variabel memori heap ke dalam memori lokal thread untuk membuat salinan dari variabel. Setelah itu, thread tidak ada hubungannya dengan nilai variabel objek di memori heap. Sebaliknya, thread langsung mengubah nilai variabel copy. nilai variabel thread copy secara otomatis ditulis kembali ke variabel objek di heap. Dengan cara ini, nilai objek di heap berubah. Gambar di bawah
Jelaskan interaksi ini
membaca dan memuat variabel salinan dari memori utama ke memori kerja saat ini
menggunakan dan menetapkan kode eksekusi dan mengubah nilai variabel bersama
menyimpan dan menulis penyegaran konten terkait memori utama dengan data memori kerja
di mana penggunaan dan penetapan dapat muncul berkali-kali
Namun, operasi ini tidak bersifat atomik. Artinya, setelah pemuatan baca, jika variabel jumlah memori utama diubah, nilai dalam memori kerja thread tidak akan berubah karena telah dimuat, sehingga hasil perhitungan akan seperti yang diharapkan sama
Untuk variabel yang dimodifikasi secara volatil, mesin virtual JVM hanya memastikan bahwa nilai yang dimuat dari memori utama ke memori kerja thread adalah yang terbaru
Misalnya, jika thread 1 dan thread 2 menemukan bahwa nilai hitungan di memori utama adalah 5 selama operasi baca dan muat, maka keduanya akan memuat nilai terbaru.
Setelah jumlah heap thread 1 diubah, maka akan ditulis ke memori utama, dan variabel count di memori utama akan menjadi 6.
Karena thread 2 telah melakukan operasi baca dan muat, setelah melakukan operasi, thread ini juga akan memperbarui nilai variabel jumlah memori utama menjadi 6.
Akibatnya, setelah dua thread dimodifikasi tepat waktu menggunakan kata kunci volatil, konkurensi masih akan terjadi.