„Здравей, Амиго! Помниш ли, че Ели ти каза за проблемите, които възникват, когато няколко нишки се опитват едновременно да получат достъп до споделен ресурс, нали?“

— Да.

"Работата е там, че това не е всичко. Има още един малък проблем."

Както знаете, компютърът има памет, където се съхраняват данни и команди (code), Howто и процесор, който изпълнява тези команди и работи с данните. Процесорът чете данни от паметта, променя ги и ги записва обратно в паметта. За да ускори изчисленията, процесорът има собствена вградена "бърза" памет: кеш паметта.

Процесорът работи по-бързо, като копира най-често използваните променливи и области от паметта в своя кеш. След това прави всички промени в тази бърза памет. И след това копира данните обратно в «бавната» памет. През цялото това време бавната памет съдържа старите (непроменени!) променливи.

Тук възниква проблемът. Една нишка променя променлива , като isCancel or isInterrupted в примера по-горе, но втора нишка «не вижда тази промяна , защото се е случила в бързата памет. Това е следствие от факта, че нишките нямат достъп до кеша на другия. (Един процесор често съдържа няколко независими ядра и нишките могат да работят на физически различни ядра.)

Да си припомним вчерашния пример:

Код Описание
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");
}
}
}
Нишката «не знае», че другите нишки съществуват.

В метода run променливата isCancel се поставя в кеша на дъщерната нишка, когато се използва за първи път. Тази операция е еквивалентна на следния code:

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

Извикването на метода за отмяна от друга нишка ще промени стойността на isCancel в нормалната (бавна) памет, но не и в кешовете на други нишки.

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

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

„Уау! И измислиха ли красиво решение и за това, като например със  синхронизирано ?“

— Няма да повярвате!

Първата мисъл беше да деактивирам кеша, но това направи програмите да работят няколко пъти по-бавно. Тогава се появи друго решение.

Роди се променливата ключова дума . Поставяме тази ключова дума преди декларация на променлива, за да покажем, че нейната стойност не трябва да се поставя в кеша. По-точно, не че не можеше да се постави в кеша, а просто винаги трябваше да се чете и записва в нормалната (бавна) памет.

Ето How да коригираме нашето решение, така че всичко да работи добре:

Код Описание
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");
}
}
}
Модификаторът volatile кара дадена променлива винаги да се чете от и записва в нормална памет, споделена от всички нишки.
public static void main(String[] args)
{
Clock clock = new Clock();
Thread clockThread = new Thread(clock);
clockThread.start();

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

"Това е?"

„Това е. Просто и красиво.“