"Hai, Amigo!"

"Hai, Ellie!"

"Saya ingin memberitahu anda tentang pengubah suai yang tidak menentu. Adakah anda tahu apa itu?"

"Ada kaitan dengan benang. Saya tidak ingat betul-betul."

"Kemudian dengar. Berikut ialah beberapa butiran teknikal untuk anda:"

"Komputer mempunyai dua jenis ingatan: memori global (biasa) dan memori terbina dalam pemproses. Memori pemproses terbina dalam dibahagikan kepada daftar, cache peringkat pertama (L1), cache peringkat kedua (L2), dan peringkat ketiga (L3)."

"Jenis memori ini mempunyai kelajuan yang berbeza. Memori terpantas dan terkecil ialah daftar, kemudian cache pemproses (L1, L2, L3), dan akhirnya memori global (paling perlahan)."

"Memori global dan cache pemproses beroperasi pada kelajuan yang sangat berbeza, jadi mesin Java membenarkan setiap utas menyimpan pembolehubah yang paling kerap digunakan dalam memori utas tempatan (dalam cache pemproses)."

"Bolehkah proses ini dikawal entah bagaimana?"

"Tidak juga. Semua kerja dilakukan oleh mesin Java. Ia sangat pintar apabila ia datang untuk mengoptimumkan prestasi."

"Tetapi inilah sebabnya saya memberitahu anda perkara ini. Terdapat satu masalah kecil. Apabila dua utas berfungsi dengan pembolehubah yang sama, setiap satu boleh menyimpan salinan dalam cache setempatnya sendiri. Dan kemudian satu utas mungkin mengubah pembolehubah, tetapi yang kedua mungkin tidak melihat perubahan itu, kerana ia masih berfungsi dengan salinan pembolehubahnya sendiri."

"Nah, apa yang boleh dibuat kemudian?"

"Pencipta Java menyediakan kata kunci khas untuk situasi ini: tidak menentu. Jika pembolehubah diakses daripada benang yang berbeza, anda perlu menandakannya dengan pengubah suai yang tidak menentu, supaya mesin Java tidak memasukkannya ke dalam cache. Beginilah ia biasanya kelihatan:"

public volatile int count = 0;

"Oh, saya ingat. Awak dah cakap ni. Saya dah tahu ni."

"Mestilah. Tetapi awak ingat hanya apabila saya beritahu awak."

"Er, saya terlupa sikit."

"Pengulangan adalah ibu kepada pembelajaran!"

"Berikut adalah beberapa fakta baharu tentang pengubah yang tidak menentu. Pengubah suai yang tidak menentu hanya menjamin bahawa pembolehubah akan dibaca dan ditulis dengan selamat. Ia tidak menjamin bahawa ia akan ditukar dengan selamat."

"Apa perbezaannya?"

"Lihat bagaimana pembolehubah diubah:"

Kod Apa yang sebenarnya berlaku: Penerangan
count++
register = count;

register = register+1;

count = register;
Langkah 1.
Nilai kiraan pembolehubah disalin daripada memori global ke daftar pemproses.

Langkah 2.
Di dalam pemproses, pembolehubah daftar dinaikkan sebanyak 1.

Langkah 3.
Nilai pembolehubah disalin daripada pemproses ke memori global.

"Wah! Jadi, semua pembolehubah ditukar hanya dalam pemproses?"

"Ya."

"Dan nilai disalin ke sana ke mari: dari memori ke pemproses dan ke belakang?"

"Ya."

"Pengubah suai yang tidak menentu menjamin bahawa apabila kiraan pembolehubah diakses ia akan dibaca dari ingatan (langkah 1). Dan jika utas ingin menetapkan nilai baharu, ia pasti akan berada dalam ingatan global (langkah 3)."

"Tetapi mesin Java tidak menjamin bahawa tidak akan ada sebarang pertukaran benang antara langkah 1 dan 3."

"Jadi, menambah pembolehubah sebanyak 1 sebenarnya adalah tiga operasi?"

"Ya."

"Dan jika dua utas secara serentak mahu melaksanakan kiraan++, maka mereka boleh mengganggu satu sama lain?"

"Ya, semaknya:"

Benang 1 Benang 2 Hasilnya
register1 = count;
register1++;
count = register1;
register2 = count;
register2++;
count = register2;
register1 = count;
register2 = count;
register2++;
count = register2;
register1++;
count = register1;

"Jadi, anda boleh mengakses pembolehubah, tetapi menukarnya masih berisiko?"

"Baiklah, anda boleh mengubahnya, cuma berhati-hati ☺"

"Bagaimana?"

" segerak  adalah kawan baik kita."

"Saya faham."