"Hej, Amigo! Du minns att Ellie berättade för dig om problemen som uppstår när flera trådar försöker komma åt en delad resurs samtidigt, eller hur?"

"Ja."

"Saken är att det inte är allt. Det finns ett annat litet problem."

En dator har som bekant ett minne där data och kommandon (kod) lagras, samt en processor som utför dessa kommandon och arbetar med datan. Processorn läser data från minnet, ändrar dem och skriver tillbaka till minnet. För att påskynda beräkningarna har processorn ett eget inbyggt "snabbt" minne: cachen.

Processorn går snabbare genom att kopiera de mest använda variablerna och minnesområdena till sin cache. Sedan gör den alla ändringar i detta snabba minne. Och sedan kopierar den tillbaka data till "långsamt" minne. Allt detta medan det långsamma minnet innehåller de gamla (oförändrade!) variablerna.

Det är här problemet uppstår. En tråd ändrar en variabel , till exempel isCancel eller isInterrupted i exemplet ovan, men en andra tråd «ser inte denna förändring, eftersom det hände i det snabba minnet. Detta är en konsekvens av att trådarna inte har tillgång till varandras cache. (En processor innehåller ofta flera oberoende kärnor och trådarna kan köras på fysiskt olika kärnor.)

Låt oss komma ihåg gårdagens exempel:

Koda Beskrivning
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");
}
}
}
Tråden «vet inte» att de andra trådarna finns.

I körmetoden läggs variabeln isCancel in i den underordnade trådens cache när den används för första gången. Denna operation motsvarar följande kod:

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

Att anropa avbrytmetoden från en annan tråd kommer att ändra värdet på isCancel i normalt (långsamt) minne, men inte i andra trådars cachar.

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

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

"Wow! Och kom de på en vacker lösning för detta också, som med  synkroniserad ?"

"Du kommer inte tro det!"

Den första tanken var att inaktivera cachen, men det gjorde att programmen gick flera gånger långsammare. Sedan dök en annan lösning upp.

Det flyktiga nyckelordet föddes. Vi sätter detta nyckelord före en variabeldeklaration för att indikera att dess värde inte får placeras i cachen. Närmare bestämt var det inte så att det inte gick att lägga in i cachen, det var helt enkelt att det alltid måste läsas från och skrivas till det normala (långsamma) minnet.

Så här fixar du vår lösning så att allt fungerar bra:

Koda Beskrivning
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");
}
}
}
Den flyktiga modifieraren gör att en variabel alltid läses från och skrivs till normalt minne som delas av alla trådar.
public static void main(String[] args)
{
Clock clock = new Clock();
Thread clockThread = new Thread(clock);
clockThread.start();

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

"Det är allt?"

"Det är det. Enkelt och vackert."