"Halo, Amigo! Anda ingat bahwa Ellie memberi tahu Anda tentang masalah yang muncul ketika beberapa utas mencoba mengakses sumber daya bersama secara bersamaan, ya?"

"Ya."

"Masalahnya, bukan itu saja. Ada masalah kecil lainnya."

Seperti yang Anda ketahui, komputer memiliki memori tempat data dan perintah (kode) disimpan, serta prosesor yang menjalankan perintah ini dan bekerja dengan data tersebut. Prosesor membaca data dari memori, mengubahnya, dan menulisnya kembali ke memori. Untuk mempercepat komputasi, prosesor memiliki memori "cepat" bawaannya sendiri: cache.

Prosesor bekerja lebih cepat dengan menyalin variabel dan area memori yang paling sering digunakan ke cache-nya. Kemudian itu membuat semua perubahan dalam memori cepat ini. Dan kemudian menyalin data kembali ke memori «lambat». Selama ini, memori lambat berisi variabel lama (tidak berubah!)

Di sinilah masalah muncul. Satu utas mengubah variabel , seperti isCancel atau isInterrupted pada contoh di atas, tetapi utas kedua «tidak melihat perubahan ini , karena terjadi di memori cepat. Ini adalah konsekuensi dari fakta bahwa utas tidak memiliki akses ke cache satu sama lain. (Sebuah prosesor sering kali berisi beberapa inti independen dan utas dapat berjalan pada inti yang berbeda secara fisik.)

Mari kita ingat contoh kemarin:

Kode Keterangan
class Clock implements Runnable
{
private boolean isCancel = false;

public void cancel()
{
this.isCancel = true;
}

public void run()
{
while (!this.isCancel)
{
Thread.sleep(1000);
System.out.println("Tick");
}
}
}
Utas itu «tidak tahu» bahwa utas lainnya ada.

Dalam metode run, variabel isCancel dimasukkan ke dalam cache thread anak saat digunakan untuk pertama kali. Operasi ini setara dengan kode berikut:

public void run()
{
boolean isCancelCached = this.isCancel;
while (!isCancelCached)
{
Thread.sleep(1000);
System.out.println("Tick");
}
}

Memanggil metode pembatalan dari utas lain akan mengubah nilai isCancel di memori normal (lambat), tetapi tidak di cache utas lain.

public static void main(String[] args)
{
Clock clock = new Clock();
Thread clockThread = new Thread(clock);
clockThread.start();

Thread.sleep(10000);
clock.cancel();
}

"Whoa! Dan apakah mereka juga menemukan solusi yang bagus untuk ini, seperti dengan  sinkronisasi ?"

"Kamu tidak akan percaya!"

Pikiran pertama adalah menonaktifkan cache, tetapi ini membuat program berjalan beberapa kali lebih lambat. Kemudian solusi yang berbeda muncul.

Kata kunci volatile lahir. Kami meletakkan kata kunci ini sebelum deklarasi variabel untuk menunjukkan bahwa nilainya tidak boleh dimasukkan ke dalam cache. Lebih tepatnya, itu bukan karena tidak dapat dimasukkan ke dalam cache, hanya saja itu harus selalu dibaca dan ditulis ke memori normal (lambat).

Berikut cara memperbaiki solusi kami agar semuanya berfungsi dengan baik:

Kode Keterangan
class Clock implements Runnable
{
private volatile boolean isCancel = false;

public void cancel()
{
this.isCancel = true;
}

public void run()
{
while (!this.isCancel)
{
Thread.sleep(1000);
System.out.println("Tick");
}
}
}
Pengubah volatil menyebabkan variabel selalu dibaca dari dan ditulis ke memori normal yang digunakan bersama oleh semua utas.
public static void main(String[] args)
{
Clock clock = new Clock();
Thread clockThread = new Thread(clock);
clockThread.start();

Thread.sleep(10000);
clock.cancel();
}

"Itu dia?"

"Itu dia. Sederhana dan cantik."