"Здравей, Амиго! Имаме панацея - лек за всички болести. Както вече видяхме, неконтролираното превключване на нишки е проблем."

"Защо самите нишки не могат да решат кога да превключат към следващата нишка? Направете всичко необходимо и след това сигнализирайте, «Готово!"?"

„Позволяването на самите нишки да контролират превключването би било още по-голям проблем. Да предположим, че имате няHowъв зле написан code и нишката никога не предава процесора. Навремето това работеше така. И беше истински кошмар.“

"Добре. И така, Howво е решението?"

" Блокиране на други теми.  Ето How работи."

Стана ясно, че нишките се намесват една в друга, когато се опитват да използват споделени обекти и/or ресурси . Точно Howто видяхме в примера с конзолен изход: има една конзола и всички нишки, извеждани към нея. Разхвърляно е.

Така беше изобретен специален обект: мютексът . Това е като табела на вратата на банята, която казва „свободно / заето“ . Има две състояния: обектът е наличен or зает . Тези състояния се наричат ​​още «заключени» и «отключени».

Когато дадена нишка се нуждае от обект, споделен с други нишки, тя проверява мютекса, свързан с обекта. Ако мютексът е отключен, тогава нишката го заключва (маркира го като «зает») и започва да използва споделения ресурс. След като нишката свърши работата си, мютексът се отключва (маркиран като «достъпен»).

Ако нишката иска да използва обекта и мутексът е заключен, тогава нишката заспива, докато чака. Когато мутексът най-накрая бъде отключен от заемащата нишка, нашата нишка веднага ще я заключи и ще започне да работи. Аналогията с табела на вратата на банята е перфектна.

"И така, How да работя с mutex? Трябва ли да създавам специални обекти?"

„Много по-просто е от това. Създателите на Java са вградor този мютекс в класа Object. Така че дори не е нужно да го създавате. Той е част от всеки обект. Ето How работи всичко:“

Код Описание
class MyClass
{
private String name1 = "Ally";
private String name2 = "Lena";

public void swap()
{
synchronized (this)
{
String s = name1;
name1 = name2;
name2 = s;
}
}
}
Методът swap разменя стойностите на променливите name1 и name2.

Какво може да се случи, ако бъде извикан от две нишки едновременно?

Действително изпълнение на codeа Код на първата нишка Код на втората нишка
String s1 = name1; //Ally
name1 = name2; //Lena
name2 = s1; //Ally

String s2 = name1; //Lena
name1 = name2; //Ally
name2 = s2; //Lena
String s1 = name1;
name1 = name2;
//other thread is running
name2 = s1;
//the thread waits until the mutex is unlocked

String s2 = name1;
name1 = name2;
//other thread is running
//other thread is running
name2 = s2;
Долния ред
Стойностите на променливите бяха разменени два пъти, връщайки се на първоначалните им места.

Обърнете внимание на ключовата дума  synchronized .

"Да, Howво означава това?"

„Когато нишка влезе в блок от code, маркиран като синхронизиран, Java машината незабавно заключва мютекса на обекта, посочен в скоби след думата синхронизиран. Никоя друга нишка не може да влезе в този блок, докато нашата нишка не го напусне. Веднага щом нашата нишка напусне блокът е маркиран като синхронизиран, мютексът незабавно и автоматично се отключва и ще бъде достъпен за придобиване от друга нишка."

Ако мютексът е зает, нашата нишка ще стои неподвижна и ще чака да се освободи.

„Толкова просто и толкова елегантно. Това е красиво решение.“

"Да. Но Howво мислите, че ще се случи в този случай?"

Код Описание
class MyClass
{
private String name1 = "Ally";
private String name2 = "Lena";

public void swap()
{
synchronized (this)
{
String s = name1;
name1 = name2;
name2 = s;
}
}

public void swap2()
{
synchronized (this)
{
String s = name1;
name1 = name2;
name2 = s;
}
}
}
Методите swap и swap2 споделят един и същ mutex ( този обект).

Какво се случва, ако една нишка извика метода swap, а друга нишка извика метода swap2?

„Тъй като mutex е същият, втората нишка ще трябва да изчака, докато първата нишка напусне синхронизирания блок. Така че няма да има проблеми с едновременния достъп.“

"Браво, Амиго! Това е верният отговор!"

Сега бих искал да отбележа, че synchronized може да се използва за маркиране не само на блокове от code, но и на методи. Ето Howво означава това:

Код Какво наистина се случва
class MyClass
{
private static String name1 = "Ally";
private static String name2 = "Lena";

public synchronized void swap()
{
String s = name1;
name1 = name2;
name2 = s;
}

public static synchronized void swap2()
{
String s = name1;
name1 = name2;
name2 = s;
}
}
class MyClass
{
private static String name1 = "Ally";
private static String name2 = "Lena";

public void swap()
{
synchronized (this)
{
String s = name1;
name1 = name2;
name2 = s;
}
}

public static void swap2()
{
synchronized (MyClass.class)
{
String s = name1;
name1 = name2;
name2 = s;
}
}