Java Semaphore availablePermits with ThreadPool

Rajkumar Natarajan :

I'm learning semaphore in java using the tutorial https://www.baeldung.com/java-semaphore.

The first test in the tutorial (which is below one) is working fine.

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

If I comment the line executorService.shutdown(); in the above test case snippet then the test fails with below error.

java.lang.AssertionError: 
Expected :0
Actual   :1
<Click to see difference>
at org.testng.AssertJUnit.fail(AssertJUnit.java:59)
at org.testng.AssertJUnit.failNotEquals(AssertJUnit.java:364)
at org.testng.AssertJUnit.assertEquals(AssertJUnit.java:80)
at org.testng.AssertJUnit.assertEquals(AssertJUnit.java:245)
at org.testng.AssertJUnit.assertEquals(AssertJUnit.java:252)
at LoginQueueSemaphoreTest.givenLoginQueue_whenReachLimit_thenBlocked(LoginQueueSemaphoreTest.java:24)

Once I remove the comment and enable the executorService.shutdown(); the test works fine.

I couldn't understand how the test(Semaphore available permits) depends on the external thread pool. Also, how is 1 slot available when I didn't release any permits? Any brief explanation would be helpful. Thanks in advance.

Janus Varmarken :

Is this consistent behavior, or do you sometimes get a different value for Actual (when omitting the executorService.shutdown() call)? If you occasionally see other values (say 2 or 3), this strongly suggests that this is all due to scheduling/timing.

Try replacing executorService.shutdown() with Thread.sleep(15_000). Does the test pass if you make this change? If yes, then this indicates that what happens is that the extra time it takes to also execute the shutdown() call is just enough to make sure that the 10 threads can be spawned and perform their work (calling tryLogin() to decrement the Semaphore) before the assertEquals() call is executed. Note that, as per the documentation, shutdown() initiates an orderly shutdown of the ExecutorService in that it lets executing tasks run to completion, but the call itself does not block during that period. As a result, it seems like it is purely by chance that the time it takes to execute shutdown() is just enough to allow the other threads to terminate. As such, this is not a very good test as that behavior may vary across different machines. Instead, the test should have called shutdown() followed by awaitTermination() (or alternatively used a CountDownLatch decremented by the workers to block the main thread) to ensure that the assertEquals() call is not executed until after all the worker threads have terminated.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=98413&siteId=1