"Merhaba, Amigo! Ellie'nin size birkaç ileti dizisi aynı anda paylaşılan bir kaynağa erişmeye çalıştığında ortaya çıkan sorunlardan bahsettiğini hatırlıyorsunuz, değil mi?"

"Evet."

"Sorun şu ki, hepsi bu kadar değil. Küçük bir sorun daha var."

Bildiğiniz gibi bir bilgisayar, verilerin ve komutların (kodların) depolandığı bir belleğe ve bu komutları yürüten ve verilerle çalışan bir işlemciye sahiptir. İşlemci, verileri bellekten okur, değiştirir ve yeniden belleğe yazar. İşlemcinin hesaplamaları hızlandırmak için kendi yerleşik "hızlı" belleği vardır: önbellek.

İşlemci, en sık kullanılan değişkenleri ve bellek alanlarını önbelleğine kopyalayarak daha hızlı çalışır. Daha sonra bu hızlı bellekte tüm değişiklikleri yapar. Ardından verileri "yavaş" belleğe geri kopyalar. Bütün bunlar olurken, yavaş bellek eski (değişmemiş!) değişkenleri içerir.

Sorunun ortaya çıktığı yer burasıdır. Bir iş parçacığı , yukarıdaki örnekte isCancel veya isInterrupted gibi bir değişkeni değiştirir , ancak ikinci bir iş parçacığı , hızlı bellekte gerçekleştiği için «bu değişikliği görmez . Bu, iş parçacıklarının birbirlerinin önbelleğine erişimlerinin olmamasının bir sonucudur. (Bir işlemci genellikle birkaç bağımsız çekirdek içerir ve iş parçacıkları fiziksel olarak farklı çekirdeklerde çalışabilir.)

Dünkü örneği hatırlayalım:

kod Tanım
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");
}
}
}
Konu, diğer konuların var olduğunu "bilmiyor".

run yönteminde, isCancel değişkeni ilk kez kullanıldığında alt iş parçacığının önbelleğine konur. Bu işlem aşağıdaki koda eşdeğerdir:

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

İptal yönteminin başka bir iş parçacığından çağrılması, normal (yavaş) bellekte isCancel değerini değiştirir , ancak diğer iş parçacıklarının önbelleklerinde değiştirmez.

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

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

"Vay canına! Ve bunun için de güzel bir çözüm buldular mı, örneğin  senkronize ?"

"İnanmayacaksın!"

İlk düşünce önbelleği devre dışı bırakmaktı, ancak bu, programların birkaç kat daha yavaş çalışmasına neden oldu. Sonra farklı bir çözüm ortaya çıktı.

Uçucu anahtar kelime doğdu. Bu anahtar sözcüğü, değerinin önbelleğe alınmaması gerektiğini belirtmek için bir değişken bildiriminin önüne koyarız. Daha doğrusu, önbelleğe alınamayacağından değil, her zaman normal (yavaş) bellekten okunması ve buraya yazılması gerekiyordu.

Her şeyin yolunda gitmesi için çözümümüzü nasıl düzelteceğiniz aşağıda açıklanmıştır:

kod Tanım
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");
}
}
}
Uçucu değiştirici, bir değişkenin her zaman tüm iş parçacıkları tarafından paylaşılan normal bellekten okunmasına ve bu belleğe yazılmasına neden olur.
public static void main(String[] args)
{
Clock clock = new Clock();
Thread clockThread = new Thread(clock);
clockThread.start();

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

"Bu kadar?"

"İşte bu. Basit ve güzel."