Implementing a Semaphore in Java


Today we will take a glimpse at semaphores and have a look at an implementation based on locks.

What is a semaphore?

A semaphore is basically a lock that allows multiple processes to enter a critical section at the same time. As an example one could see car park barriers as semaphores. If a car park has 100 lots, the barrier will only allow 100 cars into the car park at the same time. After a car has left, we expect the barrier to allow a new car in.

We can take the exact same idea (namely the car park barrier) and use it in parallel programming. Assume we have a database that can handle no more than 10 users accessing it concurrently, one would solve this by creating a semaphore with a capacity of 10 and every thread trying to access the database would first have to acquire the semaphore and after accessing the database release it again.

How can we implement one in Java?

Before you copy and paste the code below I want to warn you that this is a terrible idea. Java already has semaphores built-in which are well tested and safe to use. Although the following implementation is correct you should not use it in real project.

The semaphore below is counter based. It has a counter that can only be accessed when a corresponding lock has been locked; this prevents data races. But what happens when we cannot increase the counter since we have already enough threads in the critical section? For this we use conditions. If we are in such a case we call asleep on the condition that defined on the lock. When a thread exits the critical section it will call signalAll() and the sleeping threads will awake. Before the threads that are active again can enter they must check if the count allows them to enter, only they are allowed will they enter.

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;


public class MySemaphore {

    private volatile int count;

    private ReentrantLock lock;
    private Condition inUse;
    
    /**
     * Constructor constructs a new semaphore.
     * @param maxCount - the number of threads that can maximally enter the critical section at once.
     */
    public MySemaphore(int maxCount) {
        count = maxCount;
        lock = new ReentrantLock();
        inUse = lock.newCondition();
    }
    
    /**
     * Will acquire the semaphore. If we do not have enough space we will wait until there is space.
     * @throws InterruptedException
     */
    public void acquire() throws InterruptedException {
        try {
            lock.lock();
            while(!(count > 0)) {
                inUse.await();
            }
            count--;
        } finally {
            lock.unlock();          
        }
    }

    /**
     * Will release the semaphore.
     */
    public void release() {
        try {
            lock.lock();
            count++;
            inUse.signalAll();  
        } finally {
            lock.unlock();          
        }
    }

}

Comments


Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
© 2020