How will you implement your custom threadsafe Semaphore in Java

Carvia Tech | 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.

Java Docs: Condition and Lock Interface

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.

Not for production use, instead use java.util.concurrent.Semaphore
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();
        }
    }

}
1 Derived from Java Concurrency in Practice: Chapter14. BuildingCustomSynchronizers

Code walk-through

  1. A semaphore contains a number of permits and provides two methods - acquire() and release()

  2. 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 through Condition interface (which is permitsAvailable in our case)

  3. 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.

  4. As you can see, both methods (acquire and release) call lock() on lock object, this is necessary for memory visibility and atomicity of shared mutable state (permits in this case)


Top articles in this category:
  1. How will you increment each element of an Integer array, using parallel operation
  2. Explain the threading Jargon in Java
  3. Custom Thread pool implementation in Java
  4. ThreadLocal with examples in Java
  5. Java 8 Parallel Stream custom ThreadPool
  6. Discuss internals of a ConcurrentHashmap (CHM) in Java
  7. Troubleshooting Deadlock in Java

Find more on this topic:

Recommended books for interview preparation: