Semaforer i Java

1. Oversikt

I denne raske opplæringen vil vi utforske det grunnleggende om semaforer og mutexer i Java.

2. Semafor

Vi begynner med java.util.concurrent.Semaphore. Vi kan bruke semaforer for å begrense antall samtidige tråder som får tilgang til en bestemt ressurs.

I det følgende eksemplet vil vi implementere en enkel påloggingskø for å begrense antall brukere i systemet:

klasse LoginQueueUsingSemaphore {private Semaphore semaphore; offentlig LoginQueueUsingSemaphore (int slotLimit) {semaphore = new Semaphore (slotLimit); } boolsk tryLogin () {return semaphore.tryAcquire (); } ugyldig avlogging () {semaphore.release (); } int availableSlots () {return semaphore.availablePermits (); }}

Legg merke til hvordan vi brukte følgende metoder:

  • tryAcquire () - returner sann hvis en tillatelse er tilgjengelig umiddelbart og skaffe den ellers returnere falsk, men tilegne() får tillatelse og sperring til en er tilgjengelig
  • frigjør () - frigjør tillatelse
  • availablePermits () - retur antall nåværende tilgjengelige tillatelser

For å teste påloggingskøen vår, vil vi først prøve å nå grensen og sjekke om neste påloggingsforsøk blir blokkert:

@Test offentlig ugyldig givenLoginQueue_whenReachLimit_thenBlocked () {int slots = 10; ExecutorService executorService = Executors.newFixedThreadPool (slots); LoginQueueUsingSemaphore loginQueue = ny LoginQueueUsingSemaphore (slots); IntStream.range (0, slots) .forEach (user -> executorService.execute (loginQueue :: tryLogin)); executorService.shutdown (); assertEquals (0, loginQueue.availableSlots ()); assertFalse (loginQueue.tryLogin ()); }

Deretter vil vi se om noen spor er tilgjengelige etter en avlogging:

@Test offentlig ugyldig givenLoginQueue_whenLogout_thenSlotsAvailable () {int slots = 10; ExecutorService executorService = Executors.newFixedThreadPool (slots); LoginQueueUsingSemaphore loginQueue = ny LoginQueueUsingSemaphore (slots); IntStream.range (0, slots) .forEach (user -> executorService.execute (loginQueue :: tryLogin)); executorService.shutdown (); assertEquals (0, loginQueue.availableSlots ()); loginQueue.logout (); assertTrue (loginQueue.availableSlots ()> 0); assertTrue (loginQueue.tryLogin ()); }

3. Tidsbestemt Semafor

Deretter vil vi diskutere Apache Commons TimedSemaphore. TimedSemaphore tillater et antall tillatelser som en enkel semafor, men i en gitt tidsperiode, etter denne perioden tilbakestilles tiden og alle tillatelser frigjøres.

Vi kan bruke TimedSemaphore å bygge en enkel forsinkelseskø som følger:

class DelayQueueUsingTimedSemaphore {private TimedSemaphore semaphore; DelayQueueUsingTimedSemaphore (long period, int slotLimit) {semaphore = new TimedSemaphore (period, TimeUnit.SECONDS, slotLimit); } boolsk tryAdd () {return semaphore.tryAcquire (); } int availableSlots () {return semaphore.getAvailablePermits (); }}

Når vi bruker en forsinkelseskø med ett sekund som tidsperiode og etter å ha brukt alle sporene innen ett sekund, bør ingen være tilgjengelige:

offentlig ugyldig givenDelayQueue_whenReachLimit_thenBlocked () {int slots = 50; ExecutorService executorService = Executors.newFixedThreadPool (slots); DelayQueueUsingTimedSemaphore delayQueue = new DelayQueueUsingTimedSemaphore (1, slots); IntStream.range (0, slots) .forEach (user -> executorService.execute (delayQueue :: tryAdd)); executorService.shutdown (); assertEquals (0, delayQueue.availableSlots ()); assertFalse (delayQueue.tryAdd ()); }

Men etter å ha sovet en stund, semaforen skal tilbakestilles og frigjøre tillatelsene:

@Test offentlig ugyldighet givenDelayQueue_whenTimePass_thenSlotsAvailable () kaster InterruptedException {int slots = 50; ExecutorService executorService = Executors.newFixedThreadPool (slots); DelayQueueUsingTimedSemaphore delayQueue = new DelayQueueUsingTimedSemaphore (1, slots); IntStream.range (0, slots) .forEach (user -> executorService.execute (delayQueue :: tryAdd)); executorService.shutdown (); assertEquals (0, delayQueue.availableSlots ()); Tråd. Søvn (1000); assertTrue (delayQueue.availableSlots ()> 0); assertTrue (delayQueue.tryAdd ()); }

4. Semaphore vs. Mutex

Mutex fungerer på samme måte som en binær semafor, vi kan bruke den til å implementere gjensidig ekskludering.

I det følgende eksemplet bruker vi en enkel binær semafor til å bygge en teller:

klasse CounterUsingMutex {privat Semaphore mutex; privat int count; CounterUsingMutex () {mutex = new Semaphore (1); telle = 0; } ugyldig økning () kaster InterruptedException {mutex.acquire (); this.count = this.count + 1; Tråd. Søvn (1000); mutex.release (); } int getCount () {returner this.count; } boolsk hasQueuedThreads () {return mutex.hasQueuedThreads (); }}

Når mange tråder prøver å få tilgang til disken på en gang, de blir ganske enkelt blokkert i kø:

@Test offentlig ugyldig nårMutexAndMultipleThreads_thenBlocked () kaster InterruptedException {int count = 5; ExecutorService executorService = Executors.newFixedThreadPool (count); CounterUsingMutex counter = new CounterUsingMutex (); IntStream.range (0, count) .forEach (user -> executorService.execute (() -> {try {counter.increase ();} catch (InterruptedException e) {e.printStackTrace ();}})); executorService.shutdown (); assertTrue (counter.hasQueuedThreads ()); }

Når vi venter, vil alle tråder få tilgang til telleren og ingen tråder igjen i køen:

@Test offentlig ugyldig givenMutexAndMultipleThreads_ThenDelay_thenCorrectCount () kaster InterruptedException {int count = 5; ExecutorService executorService = Executors.newFixedThreadPool (count); CounterUsingMutex teller = ny CounterUsingMutex (); IntStream.range (0, count) .forEach (user -> executorService.execute (() -> {try {counter.increase ();} catch (InterruptedException e) {e.printStackTrace ();}})); executorService.shutdown (); assertTrue (counter.hasQueuedThreads ()); Tråd. Søvn (5000); assertFalse (counter.hasQueuedThreads ()); assertEquals (count, counter.getCount ()); }

5. Konklusjon

I denne artikkelen utforsket vi det grunnleggende om semaforer i Java.

Som alltid er hele kildekoden tilgjengelig på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found