CodeGym /Курсове /Java Core /Локалният кеш: многонишков проблем. Летлив

Локалният кеш: многонишков проблем. Летлив

Java Core
Ниво, Урок
На разположение

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

— Да.

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

Както знаете, компютърът има памет, където се съхраняват данни и команди (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();
}

"Това е?"

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

Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION