The java.util.concurrent.Semaphore supports synchronization by providing limited access when working in a multithreaded environment in a JVM.
Sounds a lot like a lock, right? But there are two major differences between lock and semaphore −
- Semaphore does not have ownership. It can be acquired by a thread and released by another thread. Locks are tied to a thread. It needs to be released and acquired by the same thread.
- Semaphore supports entry of 1 of one or more threads into the critical section based on req based on available permits.
Similarly, ISemaphore provides a distributed version of Java Semaphore. ISemaphore provides a distributed version of Java Semaphore. It provides similar functions: acquire, release.
But a major difference between ISemaphore and Java Semaphore is that while the Java Semaphore provides protection of critical section from threads in a single JVM, ISemaphore provides synchronization for threads in a single JVM as well as multiple JVMs.
ISemaphore has one synchronous backup which means, if we have a setup where we have, say, 5 JVMs running, only two JVMs will hold this semaphore.
Acquiring Permit, Releasing Permit
Example
Let’s execute the following code on three JVMs. The code is supposed to print the number of threads that have acquired semaphore. And we have a permit of 2 which means, at a time, only two threads should be permitted to enter the if block.
public static void main(String... args) throws IOException, InterruptedException {
//initialize hazelcast instance
HazelcastInstance hazelcast = Hazelcast.newHazelcastInstance();
// create a lock
ISemaphore hzSemaphore = hazelcast.getSemaphore("semaphore_1");
IAtomicLong activeThreads = hazelcast.getAtomicLong("threads");
hzSemaphore.init(2);
for(int i=0; i< 10; i++) {
if(hzSemaphore.tryAcquire(2000, TimeUnit.MILLISECONDS)); {
System.out.println("Thread count: " +
activeThreads.incrementAndGet());
Thread.sleep(2000);
hzSemaphore.release();
activeThreads.decrementAndGet();
}
}
System.exit(0);
}
The output for the code shows that we have 1 or 2 threads active which is what we expect given the permit being set to 2.
Good Practices
- If a member which has permit goes down, the permit is released automatically, making it available for other threads to acquire.
- Avoid using acquire() of semaphore, as it is a blocking call which may lead to deadlock. It’s better to use tryAcquire() with a timeout to avoid blocking.
Useful Methods
Sr.No | Function Name & Description |
1 | acquire() Acquire the permit if available. If unavailable, it waits indefinitely till the permit is available. |
2 | release() Release the acquired permit. |
3 | tryAcquire(long time, TimeUnit unit) Try to acquire a permit in the given time window. Return true if the permit is acquired, else false. |
4 | availablePermits() Return the number of permits which are available with this ISemaphore instance |