import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ThreadSafe
public class CustomSemaphore { (1)
private final Lock lock = new ReentrantLock();
// CONDITION PREDICATE: permitsAvailable (permits > 0)
private final Condition permitsAvailable = lock.newCondition();
@GuardedBy("lock")
private int permits;
CustomSemaphore(int initialPermits) {
lock.lock();
try {
permits = initialPermits;
} finally {
lock.unlock();
}
}
/**
* Blocks until permitsAvailable (permit > 0)
* @throws InterruptedException
*/
public void acquire() throws InterruptedException {
lock.lock();
try {
while (permits <= 0)
permitsAvailable.await();
--permits;
} finally {
lock.unlock();
}
}
/**
* Release a single permit and notifies threads waiting on permitsAvailable Condition
*/
public void release() {
lock.lock();
try {
++permits;
permitsAvailable.signal();
} finally {
lock.unlock();
}
}
}
How will you implement your custom threadsafe Semaphore in Java
Upasana | May 05, 2019 | 2 min read | 837 views
We can implement a custom semaphore using ReentrantLock
and Condition
classes provided by Java. However this implementation is just for illustration purpose and not for production use.
Condition factors out the Object monitor methods (wait, notify and notifyAll) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary Lock implementations. Where a Lock replaces the use of synchronized methods and statements, a Condition replaces the use of the Object monitor methods.
1 | Derived from Java Concurrency in Practice: Chapter14. BuildingCustomSynchronizers |
Code walk-through
-
A semaphore contains a number of permits and provides two methods -
acquire()
andrelease()
-
acquire()
method is a blocking call which will decrease number of available permits by one, else wait for a permit to be available. Signalling is done throughCondition
interface (which ispermitsAvailable
in our case) -
release()
method will increment number of permits by one and notify all threads waiting on condition (permitsAvailable
), so that one of waiting thread (if any) can acquire the next lock. -
As you can see, both methods (acquire and release) call
lock()
onlock
object, this is necessary for memory visibility and atomicity of shared mutable state (permits
in this case)
Top articles in this category:
- How will you increment each element of an Integer array, using parallel operation
- Explain the threading Jargon in Java
- Custom Thread pool implementation in Java
- ThreadLocal with examples in Java
- Java 8 Parallel Stream custom ThreadPool
- Discuss internals of a ConcurrentHashmap (CHM) in Java
- Troubleshooting Deadlock in Java