Útmutató a java.util.concurrent.Locks fájlhoz

1. Áttekintés

Egyszerűen fogalmazva: a zár rugalmasabb és kifinomultabb szálszinkronizáló mechanizmus, mint a szokásos szinkronizált Blokk.

A Zár felület már a Java 1.5 óta létezik. A java.util.concurrent.lock csomagot, és kiterjedt műveleteket biztosít a reteszeléshez.

Ebben a cikkben megvizsgáljuk a Zár interfész és azok alkalmazásai.

2. A zár és a szinkronizált blokk közötti különbségek

Kevés különbség van a szinkronizált használat között Blokk és használ Zár API-k:

  • A szinkronizáltBlokk teljes mértékben benne van egy módszerben - kaphatunk Zár API-k zár() és kinyit() működés külön módszerekben
  • A synchronized blokk nem támogatja a méltányosságot, bármelyik szál megszerezheti a zárat, miután felengedett, nem adható meg preferencia. A tisztességességet elérhetjük a Zár API-k megadásával méltányosság ingatlan. Biztosítja, hogy a leghosszabb várakozó szál hozzáférést kapjon a zárhoz
  • Egy szál blokkolva van, ha nem tud hozzáférni a szinkronizálthoz Blokk. A Zár Az API biztosítja tryLock () módszer. A szál csak akkor kap zárat, ha elérhető, és nem tartja más szál. Ez csökkenti a zárra váró menet blokkolási idejét
  • Egy szál, amelyhez "várakozás" állapotban van a hozzáférés megszerzése szinkronizált blokk, nem lehet megszakítani. A Zár Az API egy módszert biztosít lockInterruptibly () amellyel megszakíthatjuk a szálat, amikor az a zárra vár

3. Zár API

Vessünk egy pillantást a Zár felület:

  • érvénytelen zár ()szerezze be a zárat, ha elérhető; ha a zár nem érhető el, akkor egy szál blokkolódik a zár felengedéséig
  • void lock Megszakítható () - ez hasonló a zár(), de lehetővé teszi a blokkolt szál megszakítását és egy dobás folytatásával folytatja a végrehajtást java.lang.InterruptedException
  • logikai tryLock ()- ez a nem blokkoló változata zár() módszer; azonnal megkísérli megszerezni a zárat, igaz vissza, ha a reteszelés sikerrel jár
  • logikai tryLock (hosszú időtúllépés, TimeUnit timeUnit)ez hasonló a tryLock (), kivéve, hogy megvárja az adott időkorlátot, mielőtt feladja a Zár
  • üres kinyit() - kinyitja a Zár példa

A lezárt példányt mindig fel kell oldani a holtpont elkerülése érdekében. A zár használatához ajánlott kódblokknak tartalmaznia kell a próbáld / fogd és végül Blokk:

Zárzár = ...; lock.lock (); próbáld meg a {// hozzáférést a megosztott erőforráshoz} végül {lock.unlock (); }

Amellett, hogy a Zár felület, nekünk van ReadWriteLock interfész, amely fenntart egy pár zárat, egyet az írásvédett műveletekhez, egyet pedig az írási műveletekhez. Az olvasási zárat egyszerre több szál is megtarthatja, amíg nincs írás.

ReadWriteLock módszereket deklarál olvasási vagy írási zárak megszerzésére:

  • A readLock () zárolásavisszaadja az olvasáshoz használt zárat
  • LockLock () zárolása - visszaadja az íráshoz használt zárat

4. Zárja be a megvalósításokat

4.1. ReentrantLock

ReentrantLock osztály hajtja végre a Zár felület. Ugyanazt az egyidejűséget és a memória szemantikáját kínálja, mint az implicit monitorzár szinkronizált módszerek és utasítások, kibővített képességekkel.

Lássuk, hogyan tudjuk használni ReenrtantLock for szinkronizálás:

public class SharedObject {// ... ReentrantLock lock = new ReentrantLock (); int számláló = 0; public void perform () {lock.lock (); próbáld ki a {// kritikus részt itt számolj ++; } végül {lock.unlock (); }} // ...}

Biztosítanunk kell, hogy becsomagoljuk a zár() és a kinyit() hívja a próbálkozz végre blokkolja a holtpont helyzetének elkerülését.

Lássuk, hogyan tryLock () művek:

public void performTryLock () {// ... logikai isLockAcquired = lock.tryLock (1, TimeUnit.SECONDS); if (isLockAcquired) {próbáld meg {// Kritikus szakasz itt} végül {lock.unlock (); }} // ...} 

Ebben az esetben a szál hívása tryLock (), várni fog egy másodpercig, és feladja a várakozást, ha a zár nem áll rendelkezésre.

4.2. ReentrantReadWriteLock

ReentrantReadWriteLock osztály hajtja végre a ReadWriteLock felület.

Nézzük meg a ReadLock vagy WriteLock egy szálon:

  • Olvassa el a Zár - ha egyetlen szál sem szerezte meg az írási zárat, vagy nem kért rá, akkor több szál megszerezheti az olvasási zárat
  • Write Lock - ha egyetlen szál sem olvas, sem ír, akkor csak egy szál szerezheti meg az írási zárat

Nézzük meg, hogyan lehet használni a ReadWriteLock:

public class SynchronizedHashMapWithReadWriteLock {Map syncHashMap = új HashMap (); ReadWriteLock lock = new ReentrantReadWriteLock (); // ... Lock writeLock = lock.writeLock (); public void put (String kulcs, String érték) {try {writeLock.lock (); syncHashMap.put (kulcs, érték); } végül {writeLock.unlock (); }} ... nyilvános karakterlánc eltávolítása (karakterlánc-kulcs) {próbáld meg {writeLock.lock (); return syncHashMap.remove (kulcs); } végül {writeLock.unlock (); }} // ...}

Mindkét írási módszer esetében be kell vonnunk a kritikus részt az írási zárral, csak egy szál férhet hozzá:

Lock readLock = lock.readLock (); // ... public String get (String key) {try {readLock.lock (); return syncHashMap.get (kulcs); } végül {readLock.unlock (); }} nyilvános logikai elem tartalmazzaKey (karakterlánc-kulcs) {try {readLock.lock (); return syncHashMap.containsKey (kulcs); } végül {readLock.unlock (); }}

Mindkét olvasási módszer esetében be kell vonnunk a kritikus részt az olvasási zárral. Több szál is hozzáférhet ehhez a szakaszhoz, ha nincs folyamatban írási művelet.

4.3. StampedLock

StampedLock Java 8-ban mutat be. Támogatja az olvasási és írási zárakat is. A zárszerzési módszerek azonban visszaküldenek egy bélyegzőt, amelyet a zár feloldásához vagy annak ellenőrzéséhez használnak:

nyilvános osztály StampedLockDemo {Térképtérkép = új HashMap (); privát StampedLock zár = új StampedLock (); public void put (String kulcs, String érték) {long stamp = lock.writeLock (); próbáld ki a {map.put (kulcs, érték); } végül {lock.unlockWrite (bélyegző); }} public String get (String key) dobja az InterruptedException {long stamp = lock.readLock (); próbáld ki a {return map.get (kulcs); } végül {lock.unlockRead (bélyegző); }}}

Egy másik szolgáltatás, amelyet a StampedLock optimista zár. A legtöbbször az olvasási műveleteknek nem kell megvárniuk az írási művelet befejezését, és ennek következtében a teljes értékű olvasási zár nem szükséges.

Ehelyett frissíthetünk olvasási zárra:

public String readWithOptimisticLock (karakterlánc-kulcs) {long stamp = lock.tryOptimisticRead (); Karakterlánc értéke = map.get (kulcs); if (! lock.validate (stamp)) {stamp = lock.readLock (); próbáld ki a {return map.get (kulcs); } végül {lock.unlock (bélyegző); }} visszatérési érték; }

5. Munka Körülmények

A Feltétel osztály lehetővé teszi, hogy egy szál megvárja, amíg valamilyen feltétel bekövetkezik a kritikus szakasz végrehajtása közben.

Ez akkor fordulhat elő, amikor egy szál megszerzi a hozzáférést a kritikus szakaszhoz, de nincs megadva a működéséhez szükséges feltétel. Például egy olvasószál hozzáférhet egy megosztott sor zárjához, amelynek még mindig nincsenek elfogyasztandó adatai.

Hagyományosan a Java biztosítja várjon (), értesítsen () és értesítsenAll () szálkommunikáció módszerei. Körülmények hasonló mechanizmusokkal rendelkeznek, de ezen felül több feltételt is megadhatunk:

public class ReentrantLockWithCondition {Verem verem = új Verem (); int KAPACITÁS = 5; ReentrantLock zár = új ReentrantLock (); Feltétel stackEmptyCondition = lock.newCondition (); Feltétel stackFullCondition = lock.newCondition (); public void pushToStack (String elem) {try {lock.lock (); while (stack.size () == KAPACITÁS) {stackFullCondition.await (); } stack.push (elem); stackEmptyCondition.signalAll (); } végül {lock.unlock (); }} public String popFromStack () {try {lock.lock (); while (stack.size () == 0) {stackEmptyCondition.await (); } return stack.pop (); } végül {stackFullCondition.signalAll (); lock.unlock (); }}}

6. Következtetés

Ebben a cikkben láttuk a Zár felület és az újonnan bevezetett StampedLock osztály. Megvizsgáltuk azt is, hogy miként használhatjuk a Feltétel osztály több körülménnyel dolgozni.

Az oktatóanyag teljes kódja elérhető a GitHub oldalon.