JavaRush /Kurslar /All lectures for UZ purposes /Ko‘p oqimli muammo — mahalliy kesh. Volatile

Ko‘p oqimli muammo — mahalliy kesh. Volatile

All lectures for UZ purposes
Daraja, Dars
Mavjud

— Salom, Amigo! Elli senga bir necha ip bir vaqtning o‘zida umumiy (ulashilgan) resursga murojaat qilishi muammosi haqida gapirib berganini eslaysanmi?

— Ha.

— Xo‘sh yana bir kichik muammo bor.

Bilganingdek, kompyuterda ma’lumotlarni va buyruqlarni (kodni) saqlaydigan xotira hamda bu buyruqlarni amalga oshiradigan protsessor bor. Protsessor xotiradan ma’lumotlarni o‘qib, ularni o‘zgartiradi va yana xotiraga qaytaradi. Protsessorning ishini tezlashtirish maqsadida unga o‘zining “tez” xotirasi — keshni qo‘shganlar.

Protsessor ishini tezlashtirish uchun eng ko‘p ishlatiladigan o‘zgaruvchilarni xotira hududidan kelib keshiga ko‘chiradi va barcha o‘zgarishlarni bu tez xotirada amalga oshiradi. Keyin esa ular yana “suzma” xotiraga qaytariladi. Bu vaqt davomida suzma xotira eski(!) (o‘zgarmagan) o‘zgaruvchilar qiymatini saqlaydi.

Va bu yerda muammo yuzaga kelishi mumkin. Bir ip o‘zgaruvchini o‘zgartiradi, masalan quyidagi misoldagi isCancel yoki isInterrupted kabi, lekin ikkinchi ip bu o‘zgarishni "ko‘rmaydi", chunki o‘zgartirish tez xotirada amalga oshirilgan. Iplar bir-birining keshiga kira olmaydi. (Protsessor ko‘pincha bir nechta mustaqil yadroga ega va iplar jismonan turli yadrolarda ishlashlari mumkin.)

Kecha misolini eslaylik:

Kod Tavsif
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("Tik");
}
}
}
Ip boshqa iplarning mavjudligi haqida "bilmaydi".

Run metodida isCancel o‘zgaruvchisi birinchi marta ishlatilganida u qiz ipning keshiga o‘tkaziladi. Bu operatsiya quyidagi kodga ekvivalent:

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

Boshqa ipdan cancel metodi chaqirilganda isCancel o‘zgaruvchisining qiymatini oddiy (suzma) xotirada o‘zgartiradi, lekin boshqa iplarning keshida emas.

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

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

— Haqiqatan ham ajoyib! Bu muammo uchun ham synchronized kabi chiroyli yechim topishdimi?

— Ishonmaysan ham!

Dastlab kesh bilan ishlashni o‘chirib qo‘yishni o‘ylashdi, lekin keyin dasturlar bir necha barobar sekin ishlashini anglashdi. Keyin boshqa yechim topildi.

Maxsus volatile kalit so‘z o‘ylab topildi. Uni o‘zgaruvchi ta’rifidan oldin qo‘yish uning qiymatini keshta joylashiga ruxsat bermas edi. Aniqrog‘i, keshda joylashni taqiqlamaydi, faqat uning o‘qilishi va yozilishi har doim faqat oddiy (suzma) xotirada bo‘ladi.

Mana bizning yechimni qanday qilib to‘g‘rilash kerakligini ko‘rsatib beray:

Kod Tavsif
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("Tik");
}
}
}
Volatile modifikatori sababli o‘zgaruvchi qiymatini o‘qish va yozish har doim oddiy, barcha iplar uchun umumiy xotirada amalga oshiriladi.
public static void main(String[] args)
{
Clock clock = new Clock();
Thread clockThread = new Thread(clock);
clockThread.start();

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

— Va hammasi shugina?

— Ha. Oddiy va chiroyli.

Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION