Szemaforok Java-ban

1. Áttekintés

Ebben a gyors bemutatóban a szemaforok és a mutexek alapjait tárjuk fel a Java-ban.

2. Szemafor

Kezdjük java.util.concurrent.Semafor. Szemaforok segítségével korlátozhatjuk az adott erőforráshoz hozzáférő egyidejű szálak számát.

A következő példában egy egyszerű bejelentkezési sort hajtunk végre a rendszer felhasználói számának korlátozása érdekében:

osztály LoginQueueUsingSemaphore {privát szemafor szemafor; public LoginQueueUsingSemaphore (int slotLimit) {szemafor = új szemafor (slotLimit); } logikai tryLogin () {return szemafor.tryAcquire (); } void logout () {szemafor.release (); } int availableSlots () {return szemafor.availablePermits (); }}

Figyelje meg, hogyan használtuk a következő módszereket:

  • tryAcquire () - adja vissza a true értéket, ha az engedély azonnal rendelkezésre áll, és ezt meg kell szereznie, ellenkező esetben a false értéket adja vissza, szerez() engedélyt szerez és blokkol, amíg rendelkezésre áll
  • kiadás () - engedély kiadása
  • availablePermits () - a rendelkezésre álló engedélyek visszatérési száma

A bejelentkezési sor teszteléséhez először megpróbáljuk elérni a korlátot, és ellenőrizzük, hogy a következő bejelentkezési kísérlet blokkolva lesz-e:

@Test public void givenLoginQueue_whenReachLimit_thenBlocked () {int slot = 10; ExecutorService végrehajtóService = Executors.newFixedThreadPool (bővítőhelyek); LoginQueueUsingSemaphore loginQueue = új LoginQueueUsingSemaphore (bővítőhelyek); IntStream.range (0, slot) .forEach (felhasználó -> végrehajtóSzolgáltatás.execute (loginQueue :: tryLogin)); executorService.shutdown (); assertEquals (0, loginQueue.availableSlots ()); assertFalse (loginQueue.tryLogin ()); }

Ezután megnézzük, hogy van-e rendelkezésre álló hely a kijelentkezés után:

@Test public void givenLoginQueue_whenLogout_thenSlotsAvailable () {int slots = 10; ExecutorService végrehajtóService = Executors.newFixedThreadPool (bővítőhelyek); LoginQueueUsingSemaphore loginQueue = új LoginQueueUsingSemaphore (bővítőhelyek); IntStream.range (0, slot) .forEach (felhasználó -> végrehajtóSzolgáltatás.execute (loginQueue :: tryLogin)); executorService.shutdown (); assertEquals (0, loginQueue.availableSlots ()); loginQueue.logout (); assertTrue (loginQueue.availableSlots ()> 0); assertTrue (loginQueue.tryLogin ()); }

3. Időzített Szemafor

Ezután megvitatjuk az Apache Commons-t Időzített szemafor. Időzített szemafor számos engedélyt engedélyez egyszerű szemaforként, de egy adott időszakban, ezt az időszakot követően az idő visszaállítása és az összes engedély felszabadul.

Tudjuk használni Időzített szemafor hogy egy egyszerű késleltetési sort állítson fel az alábbiak szerint:

class DelayQueueUsingTimedSemaphore {privát TimedSemaphore szemafor; DelayQueueUsingTimedSemaphore (hosszú periódus, int slotLimit) {szemafor = new TimedSemaphore (periódus, TimeUnit.SECONDS, slotLimit); } logikai tryAdd () {return szemafor.tryAcquire (); } int availableSlots () {return semaphore.getAvailablePermits (); }}

Ha egy késleltetési sort használunk egy másodperccel, mint időtartamot, és miután az összes helyet egy másodpercen belül felhasználjuk, egyiknek sem szabad rendelkezésre állnia:

public void givenDelayQueue_whenReachLimit_thenBlocked () {int slot = 50; ExecutorService végrehajtóService = Executors.newFixedThreadPool (bővítőhelyek); DelayQueueUsingTimedSemaphore delayQueue = új DelayQueueUsingTimedSemaphore (1, bővítőhelyek); IntStream.range (0, slot) .forEach (felhasználó -> végrehajtóSzolgáltatás.execute (delayQueue :: tryAdd)); executorService.shutdown (); assertEquals (0, delayQueue.availableSlots ()); assertFalse (delayQueue.tryAdd ()); }

De miután aludtam egy ideig, a szemafornak vissza kell állítania és ki kell engednie az engedélyeket:

@Test public void givenDelayQueue_whenTimePass_thenSlotsAvailable () thrugs InterruptedException {int slots = 50; ExecutorService végrehajtóService = Executors.newFixedThreadPool (bővítőhelyek); DelayQueueUsingTimedSemaphore delayQueue = új DelayQueueUsingTimedSemaphore (1, bővítőhelyek); IntStream.range (0, slot) .forEach (felhasználó -> végrehajtóSzolgáltatás.execute (delayQueue :: tryAdd)); executorService.shutdown (); assertEquals (0, delayQueue.availableSlots ()); Szál.alszik (1000); assertTrue (delayQueue.availableSlots ()> 0); assertTrue (delayQueue.tryAdd ()); }

4. Szemafor és Mutex

A Mutex hasonlóan működik, mint egy bináris szemafor, felhasználhatjuk a kölcsönös kirekesztés megvalósítására.

A következő példában egy egyszerű bináris szemafor segítségével fogunk számlálót építeni:

osztály CounterUsingMutex {privát szemafor mutex; private int count; CounterUsingMutex () {mutex = új szemafor (1); szám = 0; } void növekedés () dob InterruptedException {mutex.acquire (); ez.szám = ez.szám + 1; Szál.alszik (1000); mutex.release (); } int getCount () {adja vissza ezt a számot; } logikai hasQueuedThreads () {return mutex.hasQueuedThreads (); }}

Amikor sok szál megpróbálja egyszerre elérni a számlálót, egyszerűen blokkolják őket egy sorban:

@Test public void, amikorMutexAndMultipleThreads_thenBlocked () dobja az InterruptedException {int count = 5; ExecutorService executorService = Executors.newFixedThreadPool (count); CounterUsingMutex számláló = new CounterUsingMutex (); IntStream.range (0, count) .forEach (user -> executorService.execute (() -> {try {counter.increase ();} catch (InterruptedException e) {e.printStackTrace ();}})); executorService.shutdown (); assertTrue (számláló.hasQueuedThreads ()); }

Amikor várunk, az összes szál hozzáfér a számlálóhoz, és egyetlen szál sem marad a sorban:

@Test public void givenMutexAndMultipleThreads_ThenDelay_thenCorrectCount () thrugs InterruptedException {int count = 5; ExecutorService executorService = Executors.newFixedThreadPool (count); CounterUsingMutex számláló = new CounterUsingMutex (); IntStream.range (0, count) .forEach (user -> executorService.execute (() -> {try {counter.increase ();} catch (InterruptedException e) {e.printStackTrace ();}})); executorService.shutdown (); assertTrue (számláló.hasQueuedThreads ()); Szál.alszik (5000); assertFalse (számláló.hasQueuedThreads ()); assertEquals (count, counter.getCount ()); }

5. Következtetés

Ebben a cikkben a Java szemaforjainak alapjait tártuk fel.

Mint mindig, a teljes forráskód elérhető a GitHubon.