Jay Abhani
Senior Web Development Instructor at almaBetter
Master Bootstrap with this quick-reference cheat sheet. Includes essential classes, components, and layout utilities for responsive design
Concurrency is very important in modern operating systems. With the rise of multiprocessor systems as well as parallel execution one of the fundamental tools to maintain the synchronization of processes has become the semaphore. Whether you're learning about OS concepts, trying to prepare for a job interview, or writing concurrent programs in Java you'll need to know about the semaphores in OS.
This article deals with what is semaphore in OS, types, usage (different to mutexes compared) and actual implementations.
A semaphore in operating system is a synchronization primitive that manages access to shared resources by multiple processes in a concurrent system. It is basically a shared integer variable that allows access to be controlled by enforcing constraints on its current value.
The term " semaphore " comes from signalling mechanisms in railways and ships, and in computing is very much like that: it 's a signal to coordinate activities among processes.
A semaphore is a shared integer variable that can be manipulated using two atomic operations: wait(*v) and signal(*p) (in some literature these are P(3) and V(4)). They are used to manage availability of resources.
In a multiprogramming environment there may be many processes that want to access the same resource ( such as a printer or a file ), and as long as there is no control they can cause race conditions, inconsistent data or even deadlock.
Semaphores are used to:
Avoid race conditions
Provide mutual exclusion
Ensure synchronization between processes
Coordinate resource sharing safely
A semaphore in OS uses two primary atomic operations:
wait(S) {
while(S <= 0); // busy wai
S =
}
If the semaphore value is positive, it decrements the value and continues.
If it is zero or negative, the process waits until it becomes positive.
signal(S) {
S = S + 1;
}
Increments the value of the semaphore.
Wakes up a waiting process if there is one.
There are two main types of semaphores in OS:
Can only take two values: 0 and 1.
Used primarily for mutual exclusion.
Acts like a mutex.
Can take any non-negative integer value.
Used to control access to a resource that has a limited number of instances.
Commonly used in resource pooling (e.g., a database connection pool).
A common confusion arises between semaphores and mutexes. Here's a breakdown:
Feature | Semaphore | Mutex |
---|---|---|
Ownership | Not owned by any thread/process | Owned by the thread that locks it |
Value Range | Integer (can be >1) | Binary (only locked/unlocked) |
Types | Binary, Counting | Only one type |
Use Case | Signaling and resource management | Mutual exclusion only |
Blocking | May or may not block (based on logic) | Automatically blocks if not free |
mutex vs semaphore: Use mutex when only one thread can access a resource; use semaphore for signaling and resource limits.
Semaphores coordinate processes that have to happen in a certain order. You could use it to make sure a producer writes to a buffer before a consumer reads it.
Binary semaphores work like locks, only one process can see a particular section at a time.
This is to control access to resources like database connections where the total number of semaphores is fixed.
Java provides built-in support for semaphores via the java.util.concurrent.Semaphore class. Here's an example of a counting semaphore:
import java.util.concurrent.Semaphore;
public class SharedResource {
static Semaphore semaphore = new Semaphore(3); // allow 3 permits
static class Worker extends Thread {
public void run() {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " acquired a permit.");
Thread.sleep(1000); // simulate work
System.out.println(Thread.currentThread().getName() + " releasing permit.");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
for (int i = 1; i <= 6; i++) {
new Worker().start();
}
}
}
Output:
Thread-0 acquired a permit.
Thread-1 acquired a permit.
Thread-2 acquired a permit.
...
This example simulates 6 threads competing for 3 permits—a real-world usage of counting semaphores.
Despite their usefulness, semaphores have certain downsides:
Busy waiting (in spinlocks or naive implementation)
Can lead to priority inversion
Improper use can result in deadlocks or resource starvation
Hence, semaphores must be used carefully and correctly.
Efficiently handles multiple resource management
Enables inter-process communication
Helps in avoiding race conditions
Supports multiple process synchronization
Always pair wait() with signal() to avoid deadlock
Prefer higher-level abstractions like mutexes or monitors when available
Use binary semaphores for mutual exclusion, counting semaphores for resource pools
Semaphores in operating system A semaphores is an important part of the OS whereby processes and threads can be safely and synchronized execution. If you are working with concurrent system like C++, threads in Java or multi-threaded system you must understand about types of semaphores and how they are implemented and the use cases.
For mutual exclusion in OS use binary semaphores and for resource sharing use counting semaphores You can develop stable and race-free applications using binary semaphore Remember semaphore is shared integer variable that makes synchronization in many modern computing system