- OS - Home
- OS - Overview
- OS - History
- OS - Evolution
- OS - Functions
- OS - Components
- OS - Structure
- OS - Architecture
- OS - Services
- OS - Properties
- Process Management
- Processes in Operating System
- States of a Process
- Process Schedulers
- Process Control Block
- Operations on Processes
- Process Suspension and Process Switching
- Process States and the Machine Cycle
- Inter Process Communication (IPC)
- Context Switching
- Threads
- Types of Threading
- Multi-threading
- System Calls
- Scheduling Algorithms
- Process Scheduling
- Types of Scheduling
- Scheduling Algorithms Overview
- FCFS Scheduling Algorithm
- SJF Scheduling Algorithm
- Round Robin Scheduling Algorithm
- HRRN Scheduling Algorithm
- Priority Scheduling Algorithm
- Multilevel Queue Scheduling
- Lottery Scheduling Algorithm
- Starvation and Aging
- Turn Around Time & Waiting Time
- Burst Time in SJF Scheduling
- Process Synchronization
- Process Synchronization
- Solutions For Process Synchronization
- Hardware-Based Solution
- Software-Based Solution
- Critical Section Problem
- Critical Section Synchronization
- Mutual Exclusion Synchronization
- Mutual Exclusion Using Interrupt Disabling
- Peterson's Algorithm
- Dekker's Algorithm
- Bakery Algorithm
- Semaphores
- Binary Semaphores
- Counting Semaphores
- Mutex
- Turn Variable
- Bounded Buffer Problem
- Reader Writer Locks
- Test and Set Lock
- Monitors
- Sleep and Wake
- Race Condition
- Classical Synchronization Problems
- Dining Philosophers Problem
- Producer Consumer Problem
- Sleeping Barber Problem
- Reader Writer Problem
- OS Deadlock
- Introduction to Deadlock
- Conditions for Deadlock
- Deadlock Handling
- Deadlock Prevention
- Deadlock Avoidance (Banker's Algorithm)
- Deadlock Detection and Recovery
- Deadlock Ignorance
- Resource Allocation Graph
- Livelock
- Memory Management
- Memory Management
- Logical and Physical Address
- Contiguous Memory Allocation
- Non-Contiguous Memory Allocation
- First Fit Algorithm
- Next Fit Algorithm
- Best Fit Algorithm
- Worst Fit Algorithm
- Buffering
- Fragmentation
- Compaction
- Virtual Memory
- Segmentation
- Buddy System
- Slab Allocation
- Overlays
- Free Space Management
- Locality of Reference
- Paging and Page Replacement
- Paging
- Demand Paging
- Page Table
- Page Replacement Algorithms
- Optimal Page Replacement Algorithm
- Belady's Anomaly
- Thrashing
- Storage and File Management
- File Systems
- File Attributes
- Structures of Directory
- Linked Index Allocation
- Indexed Allocation
- Disk Scheduling Algorithms
- FCFS Disk Scheduling
- SSTF Disk Scheduling
- SCAN Disk Scheduling
- LOOK Disk Scheduling
- I/O Systems
- I/O Hardware
- I/O Software
- I/O Programmed
- I/O Interrupt-Initiated
- Direct Memory Access
- OS Types
- OS - Types
- OS - Batch Processing
- OS - Multiprocessing
- OS - Hybrid
- OS - Monolithic
- OS - Zephyr
- OS - Nix
- OS - Linux
- OS - Blackberry
- OS - Garuda
- OS - Tails
- OS - Clustered
- OS - Haiku
- OS - AIX
- OS - Solus
- OS - Tizen
- OS - Bharat
- OS - Fire
- OS - Bliss
- OS - VxWorks
- OS - Embedded
- OS - Single User
- Miscellaneous Topics
- OS - Security
- OS Questions Answers
- OS - Questions Answers
- OS Useful Resources
- OS - Quick Guide
- OS - Useful Resources
- OS - Discussion
Operating System - Sleeping Barber Problem
The Sleeping Barber Problem is a classic synchronization problem in operating systems to show the challenges of process synchronization and inter-process communication. Read this chapter to understand the problem statement, its solution using semaphores, and the implementation details.
Sleeping Barber Problem Explained
In the Sleeping Barber Problem, we have a barber shop with one barber, one barber chair, and a waiting room with a N number of chairs for waiting customers.
- When a customer comes to the barber shop, they check if there are any free chairs in the waiting room.
- If there is a free chair, the customer sits in the waiting room. If all chairs are occupied, the customer leaves the shop.
- If the barber is busy cutting hair, the customer waits in the waiting room.
- If there are no customers, the barber goes to sleep in the barber chair.
- When a customer arrives and finds the barber sleeping, they wake up the barber to get their hair cut.
The diagram below illustrates the Sleeping Barber Problem −
In this problem, our task is to ensure that the barber and customers are synchronized properly such that −
- The barber sleeps when there are no customers.
- Customers wait if the barber is busy.
- No customer leaves the shop if there is a free chair in the waiting room.
Solution of Sleeping Barber Problem
To solve the Sleeping Barber Problem, we can use semaphores to manage access to the barber and waiting room. We can use the following semaphores:
- waitingRoom − This semaphore keeps the count of available chairs in the waiting room.
- barberChair − A binary semaphore to indicate whether the barber chair is occupied or not. It is initialized to 1 (available).
- barberSleep − This also a binary semaphore used to put the barber to sleep when there are no customers. It is initialized to 0 (sleeping).
Now, let's define the functions indicating the behavior of the barber and customers −
// Barber process
void barber() {
while (true) {
wait(barberSleep); // Sleep if there are no customers
wait(barberChair); // Acquire the barber chair
// Cut hair
signal(barberChair); // Release the barber chair
}
}
To indicate the behavior of the customer, we can define the following function −
// Customer process
void customer() {
if (wait(waitingRoom) == 0) { // Check for available chairs
signal(waitingRoom); // Leave if no chairs are available
return;
}
wait(barberChair); // Acquire the barber chair
signal(barberSleep); // Wake up the barber if sleeping
// Get hair cut
signal(barberChair); // Release the barber chair
}
In this solution, the barber waits on the barberSleep semaphore when there are no customers. When a customer arrives, they check the waitingRoom semaphore to see if there are available chairs. If a chair is available, the customer sits and wakes up the barber if necessary. The barber then cuts the customer's hair and releases the barber chair for the next customer.
Implementation of Sleeping Barber Problem
The section below implements the solution for the Sleeping Barber Problem in Python, C++, and Java.
Note: The solution uses multithreading to simulate the barber and customers as separate threads.
import threading
import time
import random
# Semaphores
waitingRoom = threading.Semaphore(3)
barberChair = threading.Semaphore(1)
barberSleep = threading.Semaphore(0)
stop_event = threading.Event()
print_lock = threading.Lock() # Prevents text overlap
# Setting print lock
def safe_print(message):
with print_lock:
print(message)
def barber():
safe_print("Barber: I am sleeping...")
while not stop_event.is_set():
# Wait for a customer
customer_arrived = barberSleep.acquire(timeout=0.5)
if stop_event.is_set():
break
if customer_arrived:
safe_print("Barber: Woke up. Cutting hair...")
time.sleep(random.uniform(0.5, 1.5)) # Cutting hair
barberChair.release() # Done
safe_print("Barber: Finished cutting hair.")
safe_print("Barber: Shop is closed. Going home.")
def customer(id):
# 1. Check if shop is closed before entering
if stop_event.is_set():
return
safe_print(f"Customer {id}: Arrived.")
# 2. Try to enter waiting room
if not waitingRoom.acquire(blocking=False):
safe_print(f"Customer {id}: Waiting room full, leaving.")
return
safe_print(f"Customer {id}: Entered waiting room.")
# 3. Wait for the barber chair
barberChair.acquire()
# Check if shop closed while we were waiting
if stop_event.is_set():
safe_print(f"Customer {id}: Sat in chair, but shop is closed! Leaving.")
barberChair.release() # Release chair immediately
waitingRoom.release() # Release waiting room spot
return
waitingRoom.release() # Leave waiting room
barberSleep.release() # Wake up the barber
safe_print(f"Customer {id}: Getting a haircut.")
# Wait for barber to finish
time.sleep(0.1) # Small buffer to let barber start
# Wait for barber to release the chair
safe_print(f"Customer {id}: Haircut done. Leaving.")
# --- Execution ---
# Start Barber
barber_thread = threading.Thread(target=barber)
barber_thread.start()
# Start Generator in background
def customer_generator():
for i in range(10):
if stop_event.is_set(): break
time.sleep(random.uniform(0.1, 0.5))
threading.Thread(target=customer, args=(i,)).start()
gen_thread = threading.Thread(target=customer_generator)
gen_thread.start()
# Run Simulation for 5 Seconds
time.sleep(5)
# Stop Everything
safe_print("\n--- 5 SECONDS UP: CLOSING SHOP ---\n")
stop_event.set()
# Cleanup
barber_thread.join()
safe_print("Simulation ended.")
The output of the above code will be similar to −
Barber: I am sleeping... Customer 0: Arrived. Customer 0: Entered waiting room. Customer 0: Getting a haircut. Barber: Woke up. Cutting hair... Customer 0: Haircut done. Leaving. Customer 1: Arrived. Customer 1: Entered waiting room. ...
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <chrono>
#include <atomic>
#include <random>
using namespace std::chrono_literals;
class Semaphore {
public:
explicit Semaphore(int initial = 0) : count(initial) {}
void release() {
std::lock_guard<std::mutex> lk(m);
++count;
cv.notify_one();
}
// blocking acquire
void acquire() {
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [&]{ return count > 0; });
--count;
}
// try acquire with timeout
bool try_acquire_for(std::chrono::milliseconds timeout) {
std::unique_lock<std::mutex> lk(m);
if (!cv.wait_for(lk, timeout, [&]{ return count > 0; })) return false;
--count;
return true;
}
// try non-blocking
bool try_acquire() {
std::lock_guard<std::mutex> lk(m);
if (count > 0) { --count; return true; }
return false;
}
int available() {
std::lock_guard<std::mutex> lk(m);
return count;
}
private:
std::mutex m;
std::condition_variable cv;
int count;
};
static Semaphore waitingRoom(3);
static Semaphore barberChair(1);
static Semaphore barberSleep(0);
static std::atomic<bool> stop_flag(false);
static std::mutex print_m;
void safe_print(const std::string &s) {
std::lock_guard<std::mutex> lk(print_m);
std::cout << s << std::endl;
}
void barber_thread_fn() {
std::mt19937 rng(std::random_device{}());
std::uniform_int_distribution<int> dist(500, 1500);
safe_print("Barber: I am sleeping...");
while (!stop_flag.load()) {
bool customerArrived = barberSleep.try_acquire_for(500ms);
if (stop_flag.load()) break;
if (customerArrived) {
safe_print("Barber: Woke up. Cutting hair...");
std::this_thread::sleep_for(std::chrono::milliseconds(dist(rng)));
barberChair.release();
safe_print("Barber: Finished cutting hair.");
}
}
safe_print("Barber: Shop is closed. Going home.");
}
void customer_fn(int id) {
if (stop_flag.load()) return;
safe_print("Customer " + std::to_string(id) + ": Arrived.");
if (!waitingRoom.try_acquire()) {
safe_print("Customer " + std::to_string(id) + ": Waiting room full, leaving.");
return;
}
safe_print("Customer " + std::to_string(id) + ": Entered waiting room.");
// Wait for barber chair, polling so we can react to stop
while (!stop_flag.load()) {
if (barberChair.try_acquire_for(200ms)) {
// got the chair
waitingRoom.release(); // left waiting room
barberSleep.release(); // wake barber
safe_print("Customer " + std::to_string(id) + ": Getting a haircut.");
std::this_thread::sleep_for(100ms); // small buffer
safe_print("Customer " + std::to_string(id) + ": Haircut done. Leaving.");
return;
}
}
// shop closed while waiting
safe_print("Customer " + std::to_string(id) + ": Shop closed while waiting, leaving.");
// release waitingRoom slot if we previously held it; but in this pattern we've already acquired it earlier,
// and either released it when getting the chair or we still hold it. For simplicity, release safely:
if (waitingRoom.available() < 3) waitingRoom.release();
}
int main() {
std::vector<std::thread> customers;
std::thread barber(barber_thread_fn);
// generator thread
std::thread gen([&](){
std::mt19937 rng(std::random_device{}());
std::uniform_int_distribution<int> genDelay(100, 500);
for (int i = 0; i < 10 && !stop_flag.load(); ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(genDelay(rng)));
customers.emplace_back(customer_fn, i);
}
});
// run 5 seconds
std::this_thread::sleep_for(5s);
safe_print("\n--- 5 SECONDS UP: CLOSING SHOP ---\n");
stop_flag.store(true);
// wake barber (in case sleeping) so it can exit promptly
barberSleep.release();
gen.join();
barber.join();
for (auto &t : customers) if (t.joinable()) t.join();
safe_print("Simulation ended.");
return 0;
}
The output of the above code will be similar to −
Barber: I am sleeping... Customer 0: Arrived. Customer 0: Entered waiting room. Customer 0: Getting a haircut. Barber: Woke up. Cutting hair... Customer 0: Haircut done. Leaving. ...
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class BarberShop {
static final int WAITING_CAPACITY = 3;
static final Semaphore waitingRoom = new Semaphore(WAITING_CAPACITY);
static final Semaphore barberChair = new Semaphore(1);
static final Semaphore barberSleep = new Semaphore(0);
static final AtomicBoolean stop = new AtomicBoolean(false);
static final ReentrantLock printLock = new ReentrantLock();
static void safePrint(String msg) {
printLock.lock();
try { System.out.println(msg); }
finally { printLock.unlock(); }
}
public static void main(String[] args) throws InterruptedException {
Random rng = new Random();
Thread barber = new Thread(() -> {
safePrint("Barber: I am sleeping...");
while (!stop.get()) {
try {
// Wait for a customer or timeout to re-check stop flag
boolean customerArrived = barberSleep.tryAcquire(500, TimeUnit.MILLISECONDS);
if (stop.get()) break;
if (customerArrived) {
safePrint("Barber: Woke up. Cutting hair...");
Thread.sleep(500 + rng.nextInt(1001)); // 500-1500ms
barberChair.release(); // done with chair
safePrint("Barber: Finished cutting hair.");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
safePrint("Barber: Shop is closed. Going home.");
}, "Barber");
List<Thread> customerThreads = new ArrayList<>();
Runnable customerTaskFactory = () -> {
int id;
synchronized (customerThreads) { id = customerThreads.size(); } // assign id
Thread t = new Thread(() -> {
if (stop.get()) return;
safePrint("Customer " + id + ": Arrived.");
// Try to enter waiting room (non-blocking)
if (!waitingRoom.tryAcquire()) {
safePrint("Customer " + id + ": Waiting room full, leaving.");
return;
}
safePrint("Customer " + id + ": Entered waiting room.");
// Wait for barber chair (polling with timeout so we can react to stop)
try {
while (!stop.get()) {
if (barberChair.tryAcquire(200, TimeUnit.MILLISECONDS)) {
// We got the chair
waitingRoom.release(); // we left waiting room to sit in chair
barberSleep.release(); // wake up barber
safePrint("Customer " + id + ": Getting a haircut.");
// The barber will release the chair after finishing the cut.
// We just wait a short time to simulate staying in chair
Thread.sleep(100); // small buffer
safePrint("Customer " + id + ": Haircut done. Leaving.");
return;
}
}
// If shop closed while waiting:
if (stop.get()) {
safePrint("Customer " + id + ": Shop closed while waiting, leaving.");
// If we still occupy waiting room slot, release it
if (waitingRoom.availablePermits() < WAITING_CAPACITY) {
// Try to release â but be cautious not to over-release:
// We only release here if we previously acquired and didn't release.
// The safe approach is to attempt to release only if we know we held it.
// In this simple example we just ensure the slot is released above when necessary.
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Customer-" + id);
synchronized (customerThreads) {
customerThreads.add(t);
}
t.start();
};
// Start barber
barber.start();
// Start generator thread that spawns some customers
Thread generator = new Thread(() -> {
for (int i = 0; i < 10 && !stop.get(); ++i) {
try {
Thread.sleep(100 + rng.nextInt(401)); // 100-500ms
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
customerTaskFactory.run();
}
}, "Generator");
generator.start();
// Let simulation run for 5 seconds
Thread.sleep(5000);
safePrint("\n--- 5 SECONDS UP: CLOSING SHOP ---\n");
stop.set(true);
// Wake barber in case it's sleeping so it can exit promptly
barberSleep.release();
// Wait for generator & barber to finish
generator.join();
barber.join();
// Join customer threads
synchronized (customerThreads) {
for (Thread t : customerThreads) {
try { t.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
}
}
safePrint("Simulation ended.");
}
}
The output of the above code will be similar to −
Barber: I am sleeping... Customer 0: Arrived. Customer 0: Entered waiting room. Barber: Woke up. Cutting hair... Customer 0: Getting a haircut. Customer 0: Haircut done. Leaving. Customer 1: Arrived. ...
Conclusion
The Sleeping Barber Problem shows how semaphores can be used to synchronize processes in a concurrent environment. To implement the solution, we just used 3 semaphores to manage access to the barber chair, waiting room, and barber sleep state. We used the concept of multithreading to simulate the barber and customers as separate threads, so that they concurrently as a separate entities similar to real-world scenario.