Java szál holtpont és Livelock

1. Áttekintés

Míg a többszálas menetrend segít az alkalmazás teljesítményének javításában, bizonyos problémákkal is jár. Ebben az oktatóanyagban két ilyen problémát fogunk megvizsgálni, a holtpontot és a megélhetést Java példák segítségével.

2. Holtpont

2.1. Mi a holtpont?

Holtpont következik be, amikor két vagy több szál örökké várja a zárat vagy erőforrást, amelyet egy másik szál tart. Következésképpen egy alkalmazás leállhat vagy meghiúsulhat, mivel a holtpontos szálak nem tudnak előrehaladni.

A klasszikus étkezési filozófusok problémája jól mutatja be a szinkronizálási kérdéseket egy többszálas környezetben, és gyakran használják a holtpontra.

2.2. Holtpont példa

Először vessünk egy pillantást egy egyszerű Java példára, hogy megértsük a holtpontot.

Ebben a példában két szálat hozunk létre, T1 és T2. cérna T1 hívások művelet1, és menet T2 hívások tevékenységek.

Műveletük befejezéséhez szál T1 megszereznie kell zár1 előbb aztán lock2, míg a menet T2 megszereznie kell zár2 előbb aztán zár1. Tehát alapvetően mindkét szál ellentétes sorrendben próbálja megszerezni a zárakat.

Most írjuk a Holtpont: Példa osztály:

public class DeadlockExample {private Lock lock1 = new ReentrantLock (true); private lock lock2 = új ReentrantLock (true); public static void main (String [] args) {DeadlockExample deadlock = new DeadlockExample (); új szál (holtpont: művelet1, "T1"). start (); új szál (holtpont: művelet2, "T2"). start (); } public void művelet1 () {lock1.lock (); print ("lock1 megszerezve, várja a lock2 megszerzését."); alvás (50); lock2.lock (); nyomtatás ("megszerzett lock2"); print ("az első művelet végrehajtása"); lock2.unlock (); lock1.unlock (); } public void művelet2 () {lock2.lock (); print ("lock2 megszerezve, várja a lock1 megszerzését."); alvás (50); lock1.lock (); nyomtatás ("megszerzett lock1"); print ("második művelet végrehajtása"); lock1.unlock (); lock2.unlock (); } // segítő módszerek}

Futtassuk most ezt a holtpontot, és vegyük észre a kimenetet:

T1 szál: lock1 megszerezve, várja a lock2 megszerzését. T2 szál: lock2 megszerezve, várja a lock1 megszerzését.

A program futtatása után láthatjuk, hogy a program holtpontot eredményez, és soha nem lép ki. A napló azt a szálat mutatja T1 vár lock2, amelyet szál tart T2. Hasonlóképpen, menet T2 vár zár1, amelyet szál tart T1.

2.3. A holtpont elkerülése

A holtpont a Java-ban gyakori egyidejűségi probléma. Ezért meg kell terveznünk egy Java alkalmazást, hogy elkerüljük az esetleges holtpontokat.

Először is kerülnünk kell több szál beszerzését egy szálhoz. Ha azonban egy szálnak több zárra van szüksége, akkor meg kell győződnünk arról, hogy minden szál ugyanabban a sorrendben szerzi be a zárakat, hogy kerülje a ciklikus függőséget a zárszerzésben.

Használhatjuk is időzített zárolási kísérletek, mint a tryLock módszer a Zár interfész, annak biztosítása érdekében, hogy egy szál ne blokkolódjon végtelenül, ha nem képes lezárást szerezni.

3. Livelock

3.1. Mi az a Livelock

A megélhetés egy másik párhuzamossági probléma, és hasonló a holtponthoz. Élőben két vagy több szál folyamatosan továbbítja az állapotokat egymás között ahelyett, hogy végtelenül várnánk, ahogy a holtpont példában láttuk. Következésképpen a szálak nem képesek elvégezni a saját feladataikat.

A megélhetés nagyszerű példája egy olyan üzenetkezelő rendszer, ahol kivétel esetén az üzenetfogyasztó visszagörgeti a tranzakciót, és visszahelyezi az üzenetet a sor elejére. Ezután ugyanazt az üzenetet ismételten elolvassák a várólistáról, csak újabb kivételt okozva, és visszahelyezik a sorba. A fogyasztó soha nem vesz más üzenetet a sorból.

3.2. Livelock példa

Most, hogy bemutassuk a megélhetési állapotot, ugyanazt a holtpontot vesszük, amelyet korábban tárgyaltunk. Ebben a példában is menet T1 hívások művelet1 és cérna T2 hívások művelet2. E műveletek logikáját azonban kissé megváltoztatjuk.

Mindkét szálnak két zárra van szüksége a munkájához. Minden szál megszerzi az első zárat, de megállapítja, hogy a második zár nem érhető el. Tehát annak érdekében, hogy a másik szál előbb teljes legyen, mindegyik szál felszabadítja az első zárat, és újra megpróbálja megszerezni mindkét zárat.

Bemutassuk a megélhetést a-val LivelockPélda osztály:

public class LivelockExample {private Lock lock1 = new ReentrantLock (true); private lock lock2 = új ReentrantLock (true); public static void main (String [] args) {LivelockExample livelock = new LivelockExample (); új szál (livelock :: operation1, "T1"). start (); új szál (livelock :: operation2, "T2"). start (); } public void művelet1 () {while (true) {tryLock (lock1, 50); print ("lock1 megszerezve, megpróbálja megszerezni a lock2-t."); alvás (50); if (tryLock (lock2)) {print ("lock2 megszerezve."); } else {print ("nem lehet megszerezni a lock2-t, feloldani a lock1-et."); lock1.unlock (); folytatni; } print ("az első művelet végrehajtása"); szünet; } lock2.unlock (); lock1.unlock (); } public void művelet2 () {while (true) {tryLock (lock2, 50); print ("lock2 megszerezve, megpróbálja megszerezni a lock1-et."); alvás (50); if (tryLock (lock1)) {print ("lock1 megszerezve."); } else {print ("nem lehet megszerezni a lock1-et, elengedni a lock2-t."); lock2.unlock (); folytatni; } print ("második művelet végrehajtása"); szünet; } lock1.unlock (); lock2.unlock (); } // segítő módszerek}

Most futtassuk ezt a példát:

T1 szál: a lock1 megszerzése, a lock2 megszerzése. T2 szál: a lock2 megszerzése, a lock1 megszerzése. T1 szál: nem tudja megszerezni a lock2-t, elengedve a lock1-et. T2 szál: nem tudja megszerezni a lock1-et, elengedve a lock2-t. T2 szál: a lock2 megszerzése, a lock1 megszerzése. T1 szál: a lock1 megszerzése, a lock2 megszerzése. T1 szál: nem tudja megszerezni a lock2-t, elengedve a lock1-et. T1 szál: a lock1 megszerzése, a lock2 megszerzése. T2 szál: nem tudja megszerezni a lock1-et, elengedve a lock2-t. ..

Amint a naplókban láthatjuk, mindkét szál ismételten megszerzi és elengedi a zárakat. Emiatt egyik szál sem képes befejezni a műveletet.

3.3. Kerülje a Livelock-ot

Az elfolyás elkerülése érdekében meg kell vizsgálnunk azt a körülményt, amely a megnövekedést okozza, majd ennek megfelelően kell megoldást találnunk.

Például, ha két olyan szálunk van, amelyek ismételten megszerzik és elengedik a zárakat, ami elárasztást eredményez, akkor úgy alakíthatjuk ki a kódot, hogy a szálak véletlenszerű időközönként megpróbálják újra megszerezni a zárakat. Ez megfelelő szálat ad a szálaknak a szükséges zárak megszerzéséhez.

Az üzenetkezelő rendszerrel kapcsolatos, korábban már tárgyalt példa életképességének kezelésének másik módja az, hogy a meghibásodott üzeneteket külön sorba helyezzük további feldolgozás céljából, ahelyett, hogy ismét ugyanabba a sorba állítanánk őket.

4. Következtetés

Ebben az oktatóanyagban a holtpontról és a megélhetésről beszélgettünk. Ezenkívül megvizsgáltuk a Java példákat, hogy bemutassuk ezeket a problémákat, és röviden kitértünk arra, hogyan kerülhetjük el ezeket.

Mint mindig, az ebben a példában használt teljes kód megtalálható a GitHubon.