Semaphore guards a shared resource allowing only a defined number of threads to operate at a time.
General behavior is as follows:
Conceptually, a Semaphore maintains a set of permits. Each acquire blocks if necessary until a permit is available, and then takes it. Each release adds a permit, potentially releasing a blocking acquirer. However, no actual permit objects are used; the Semaphore just keeps a count of the number available and acts accordingly.
Let's say, we have a restaurant and we have following requirements:
- Only 3 customers can dine at the restaurant
To achieve this, we will create a Semaphore with the permit count as 3. Each thread will acquire a permit if available. When all permits are given, other threads will wait for a permit owner to release its permit.
public class SemaphoreTest {
public static void main(String[] args) throws InterruptedException {
semaphore();
}
public static void semaphore() throws InterruptedException {
final Restaurant restaurant = new Restaurant();
final ExecutorService threadPool = Executors.newFixedThreadPool(3);
class Customer implements Runnable {
@Override
public void run() {
try {
restaurant.eat();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
threadPool.execute(new Customer());
threadPool.execute(new Customer());
threadPool.execute(new Customer());
threadPool.execute(new Customer());
threadPool.execute(new Customer());
threadPool.execute(new Customer());
threadPool.shutdown();
threadPool.awaitTermination(10, TimeUnit.SECONDS);
}
static class Restaurant {
private final Semaphore semaphore = new Semaphore(3, true);
private volatile AtomicInteger current = new AtomicInteger(0);
public void eat() throws InterruptedException {
semaphore.acquire();
try {
System.out.printf("%s - Current: %s%n", Thread.currentThread().getName(), current.incrementAndGet());
sleep(100);
} finally {
System.out.printf("%s - Will be: %s%n", Thread.currentThread().getName(), current.decrementAndGet());
semaphore.release();
}
}
}
}