π Custom Semaphore Implementation
π§ Problem Statement
Section titled βπ§ Problem StatementβThe goal is to implement a custom semaphore that:
- Initializes with the maximum number of permits (which is also the initial available permits).
- Allows threads to acquire a permit, blocking them if no permits are available.
- Allows threads to release a permit, making it available for others.
β Constraints
Section titled ββ Constraintsβ- Thread Safety: Must handle concurrent access to permits safely
- Blocking Behavior: Threads must block when no permits are available
- Permit Management: Must maintain accurate count of available permits
- Maximum Limit: Cannot exceed the initial maximum number of permits
- Notification: Must properly notify waiting threads when permits become available
- Resource Cleanup: Should handle thread interruption gracefully
π₯ Input / Output
Section titled βπ₯ Input / OutputβInput:
- Maximum number of permits during initialization
- Multiple threads calling acquire() and release() methods
- Thread interruption requests
Output:
- Controlled access to permits based on availability
- Threads blocked when no permits available
- Proper permit counting and distribution
π‘ Intuition About Solving This Problem
Section titled βπ‘ Intuition About Solving This ProblemβA semaphore controls access to a limited set of resources. Each thread that wants to access the resource must acquire a permit. If no permits are available, the thread waits. When done, the thread releases the permit.
The key insight is using a counter to track available permits and synchronization primitives to ensure thread safety. When permits are exhausted, threads wait on a condition variable until permits become available again.
π Steps to Take to Solve
Section titled βπ Steps to Take to Solveβ- Design the data structure: Maintain permit count and maximum limit
- Implement initialization: Set initial permits and validate input
- Add thread synchronization: Use synchronized blocks for thread safety
- Implement acquire logic: Block threads when no permits available
- Implement release logic: Increment permits and notify waiting threads
- Handle edge cases: Prevent permit overflow and handle interruption
β οΈ Edge Cases to Consider
Section titled ββ οΈ Edge Cases to Considerβ- Permit overflow: Prevent releasing more permits than maximum
- Negative permits: Ensure permit count never goes below zero
- Thread interruption: Handle InterruptedException properly
- Spurious wakeups: Use while loop to check permit availability
- Concurrent releases: Handle multiple threads releasing simultaneously
- Initialization errors: Validate maximum permits parameter
π» Code (with comments)
Section titled βπ» Code (with comments)βpublic class MySemaphore { private int permits; // Current available permits private final int maxPermits; // Maximum number of permits allowed private final Object lock = new Object(); // Synchronization lock
public MySemaphore(int maxPermits) { if (maxPermits <= 0) { throw new IllegalArgumentException("Permits must be greater than zero."); } this.maxPermits = maxPermits; this.permits = maxPermits; // Initialize with all permits available }
public void acquire() throws InterruptedException { synchronized (lock) { // Wait until permits become available while (permits == 0) { lock.wait(); } permits--; // Consume one permit } }
public void release() { synchronized (lock) { // Only increment if we haven't reached maximum if (permits < maxPermits) { permits++; // Make one permit available lock.notify(); // Wake up one waiting thread } } }
public int availablePermits() { synchronized (lock) { return permits; // Return current available permits } }}π Dry Run Example
Section titled βπ Dry Run ExampleβScenario: Semaphore with 3 permits, 5 threads trying to acquire
Initial State:
- Available permits: 3
- Maximum permits: 3
- 5 threads waiting to acquire
Step 1: Thread 1 acquires permit
- Check: permits > 0 β true (permits = 3)
- Decrement permits to 2
- Thread 1 continues
Step 2: Thread 2 acquires permit
- Check: permits > 0 β true (permits = 2)
- Decrement permits to 1
- Thread 2 continues
Step 3: Thread 3 acquires permit
- Check: permits > 0 β true (permits = 1)
- Decrement permits to 0
- Thread 3 continues
Step 4: Thread 4 tries to acquire permit
- Check: permits > 0 β false (permits = 0)
- Thread 4 blocks and waits
Step 5: Thread 5 tries to acquire permit
- Check: permits > 0 β false (permits = 0)
- Thread 5 blocks and waits
Step 6: Thread 1 releases permit
- Check: permits < maxPermits β true (permits = 0 < 3)
- Increment permits to 1
- Notify waiting threads
Step 7: Thread 4 wakes up and acquires permit
- Check: permits > 0 β true (permits = 1)
- Decrement permits to 0
- Thread 4 continues
Result: Only 3 threads can have permits simultaneously, others wait until permits become available.
β±οΈ Time Complexity / Space Complexity
Section titled ββ±οΈ Time Complexity / Space ComplexityβTime Complexity:
- Acquire: O(1) - constant time to check and consume permit
- Release: O(1) - constant time to increment permit and notify
- Wait time: O(1) - constant time to block/unblock threads
Space Complexity:
- Permit storage: O(1) - constant space for permit count
- Lock overhead: O(1) - constant space for synchronization object
- Thread management: O(1) - constant space regardless of number of threads
π Key Concepts
Section titled βπ Key Conceptsβ- Synchronization: Ensures thread safety when accessing
permits. - Acquire/Release: Threads block when no permits are available and are notified when permits are released.
- Fixed Permits: The semaphore limits the number of threads that can access the shared resource concurrently.
π§ͺ Usage Example
Section titled βπ§ͺ Usage Exampleβpublic class SemaphoreExample { public static void main(String[] args) throws InterruptedException { MySemaphore semaphore = new MySemaphore(3);
for (int i = 0; i < 5; i++) { final int threadId = i; new Thread(() -> { try { semaphore.acquire(); System.out.println("Thread " + threadId + " acquired a permit."); Thread.sleep(1000); semaphore.release(); System.out.println("Thread " + threadId + " released a permit."); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } }}