Javaのセマフォ

1。概要

このクイックチュートリアルでは、Javaのセマフォとミューテックスの基本について説明します。

2.セマフォ

java.util.concurrent.Semaphoreから始めますセマフォを使用して、特定のリソースにアクセスする同時スレッドの数を制限できます。

次の例では、単純なログインキューを実装して、システム内のユーザー数を制限します。

class LoginQueueUsingSemaphore { private Semaphore semaphore; public LoginQueueUsingSemaphore(int slotLimit) { semaphore = new Semaphore(slotLimit); } boolean tryLogin() { return semaphore.tryAcquire(); } void logout() { semaphore.release(); } int availableSlots() { return semaphore.availablePermits(); } }

次の方法をどのように使用したかに注目してください。

  • tryAcquire() –許可がすぐに利用できる場合はtrueを返し、それ以外の場合はfalseを返しますが、acquire()は許可を取得し、許可が利用可能になるまでブロックします
  • release()–許可を解放します
  • availablePermits()–現在利用可能な許可の数を返します

ログインキューをテストするために、最初に制限に到達しようとし、次のログイン試行がブロックされるかどうかを確認します。

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

次に、ログアウト後に使用可能なスロットがあるかどうかを確認します。

@Test public void givenLoginQueue_whenLogout_thenSlotsAvailable() { int slots = 10; ExecutorService executorService = Executors.newFixedThreadPool(slots); LoginQueueUsingSemaphore loginQueue = new 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.時限セマフォ

次に、Apache CommonsTimedSemaphoreについて説明します。TimedSemaphoreは、単純なセマフォとしていくつかの許可を許可しますが、特定の期間内に、この期間の後、時間がリセットされ、すべての許可が解放されます。

TimedSemaphoreを使用して、次のように単純な遅延キューを作成できます。

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

期間として1秒の遅延キューを使用し、1秒以内にすべてのスロットを使用した後、使用できるものはないはずです。

public void 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()); }

しかし、しばらく眠った後、セマフォはリセットして許可を解放する必要があります:

@Test public void givenDelayQueue_whenTimePass_thenSlotsAvailable() throws 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()); Thread.sleep(1000); assertTrue(delayQueue.availableSlots() > 0); assertTrue(delayQueue.tryAdd()); }

4.セマフォとミューテックス

ミューテックスはバイナリセマフォと同様に機能し、相互排除を実装するために使用できます。

次の例では、単純なバイナリセマフォを使用してカウンターを作成します。

class CounterUsingMutex { private Semaphore mutex; private int count; CounterUsingMutex() { mutex = new Semaphore(1); count = 0; } void increase() throws InterruptedException { mutex.acquire(); this.count = this.count + 1; Thread.sleep(1000); mutex.release(); } int getCount() { return this.count; } boolean hasQueuedThreads() { return mutex.hasQueuedThreads(); } }

多くのスレッドが一度にカウンターにアクセスしようとすると、それらは単にキューでブロックされます

@Test public void whenMutexAndMultipleThreads_thenBlocked() throws 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()); }

待機すると、すべてのスレッドがカウンターにアクセスし、キューにスレッドが残っていません。

@Test public void givenMutexAndMultipleThreads_ThenDelay_thenCorrectCount() throws 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()); Thread.sleep(5000); assertFalse(counter.hasQueuedThreads()); assertEquals(count, counter.getCount()); }

5。結論

この記事では、Javaのセマフォの基本について説明しました。

いつものように、完全なソースコードはGitHubで入手できます。