Home > Back-end >  Semaphore with executor service not clear about the lock mechanism
Semaphore with executor service not clear about the lock mechanism

Time:02-01

I am currently trying to understand semaphores during parallel processing. I have instantiated a semaphore of 3 permits, and have a list of strings with five values, using executor service, I have created five threads using callable interface.

Now as per my understanding it should allow processing of three threads at the same time and once any one thread has completed the process, the next thread would acquire a lock and process it.

I have the sample snippet below and output I got after execution which shows available permit as 2 at the end. It takes on the values 0, 1, 0, 1, 2 during execution.

Provided the below the code snippet and output, I still can not understand how this works by looking at the output. Can some explain what happens internally and why I am not getting number 3 in available Permits?

Also I suggest some APIs that use semaphores.

Code:

public class SemaphoreDemo {
    
    Semaphore semaphore= new Semaphore(3);

    public void doit() {
        List<String> ll =List.of("1","2","3","4","5");
        
        List<Callable<String>> parallelprocess= new ArrayList<Callable<String>>(ll.size());
        
        for(String data:ll) {
        
            parallelprocess.add(()-> {
                semaphore.acquire();
                String res= data Thread.currentThread().getName();
                Thread.sleep(1000);
                System.out.println(res);
                System.out.println(semaphore.availablePermits());
                semaphore.release();
                return res;
            });
            
        }
        
        ExecutorService executor=Executors.newFixedThreadPool(parallelprocess.size());
        
        try {
            List<Future<String>> reponse=executor.invokeAll(parallelprocess);
            
            for(Future<String> future:reponse) {
                
                try {
                    System.out.println(future.get().toString());
                } catch (ExecutionException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        executor.shutdown();
        
    }
    
    public static void main(String[] args) {
        SemaphoreDemo ss=new SemaphoreDemo();
        
        ss.doit();
    }
}

output:

3pool-1-thread-3
0
2pool-1-thread-2
1
1pool-1-thread-1
0
4pool-1-thread-4
1
5pool-1-thread-5
2
1pool-1-thread-1
2pool-1-thread-2
3pool-1-thread-3
4pool-1-thread-4
5pool-1-thread-5

CodePudding user response:

I think the output is as expected (I mean, with multithreading the result is non-deterministic but the output you presented is one of the possible outcomes).

First, multiple threads are created and start executing at the same time. 1-3 threads execute semaphore.acquire(); before the first thread (first in time, can be any element of the array of threads) calls semaphore.availablePermits(). At that time it reads the actual number of available permits. Since Thread.sleep(1000); and System.out.println(res); take very long time, the chance that this happens before any of the other threads is activated, is extremely low. Therefore the first thread ("3" in this case) gets the value 0, and prints it. Then releases a permit.

After thread "3" has released the permit, thread "2" was resumed, it asks the number of permits, which is one because e.g. thread "4" did not have enough time to call semaphore.availablePermits(). Thread "3" prints this value.

Then "4" is resumed, takes a permit but before it gets to printing, "1" is activated, asks the number of permits, which is 0 again, and prints it.

Then "4" is resumed again, asks the number of permits, prints 1 as "3", "2" and "1" have already released theirs.

Then "5" is resumed, and since it is the only one holding a permit, it prints 2.

All of these could have happened in a different ordering causing a different order in which threads print their output and different permit numbers.

However, printing a permit number of 3 is never possible because the total number of permits is 3 and semaphore.availablePermits() is always called before semaphore.release(), therefore the last thread asking this is itself holding a permit while asking, and 3 - 1 = 2.

I find your last question, "Also I suggest some APIs that use semaphores." too broad. The functions you used are part of the API, and for what you use them is quite diverse, almost like what can you do with a floating point number.

  • Related