Java Semaphore
Semaphore Nədir?
Semaphore müəyyən sayda permit (icazə) saxlayan synchronization vasitəsidir. Thread-lər permit əldə edib buraxırlar. Resursların məhdud sayda istifadəsini təmin edir.
Basic Usage
import java.util.concurrent.Semaphore;
class SemaphoreExample {
private final Semaphore semaphore = new Semaphore(2); // 2 permit
public void accessResource() throws InterruptedException {
semaphore.acquire(); // Permit al
try {
System.out.println(Thread.currentThread().getName() +
" resurs istifadə edir");
Thread.sleep(2000); // Resurs istifadəsi
} finally {
semaphore.release(); // Permit burax
}
}
}
// Test
public class Main {
public static void main(String[] args) {
SemaphoreExample example = new SemaphoreExample();
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
try {
example.accessResource();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Thread-" + i).start();
}
}
}
Database Connection Pool
class ConnectionPool {
private final Semaphore available;
private final List<Connection> connections;
private final boolean[] used;
public ConnectionPool(int size) {
available = new Semaphore(size, true); // Fair semaphore
connections = new ArrayList<>();
used = new boolean[size];
// Initialize connections
for (int i = 0; i < size; i++) {
connections.add(createConnection());
}
}
public Connection getConnection() throws InterruptedException {
available.acquire();
return getNextAvailableConnection();
}
public void returnConnection(Connection connection) {
if (markAsUnused(connection)) {
available.release();
}
}
private synchronized Connection getNextAvailableConnection() {
for (int i = 0; i < used.length; i++) {
if (!used[i]) {
used[i] = true;
return connections.get(i);
}
}
return null; // Should never happen
}
private synchronized boolean markAsUnused(Connection connection) {
int index = connections.indexOf(connection);
if (index != -1) {
used[index] = false;
return true;
}
return false;
}
}
tryAcquire() Metodları
class NonBlockingSemaphore {
private final Semaphore semaphore = new Semaphore(3);
public void tryAccess() {
if (semaphore.tryAcquire()) {
try {
System.out.println("İmmediate access granted");
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release();
}
} else {
System.out.println("Access denied - no permits available");
}
}
public void tryAccessWithTimeout() throws InterruptedException {
if (semaphore.tryAcquire(2, TimeUnit.SECONDS)) {
try {
System.out.println("Access granted after waiting");
Thread.sleep(1000);
} finally {
semaphore.release();
}
} else {
System.out.println("Timeout - access denied");
}
}
public void tryMultiplePermits() throws InterruptedException {
if (semaphore.tryAcquire(2)) { // 2 permit istə
try {
System.out.println("Got 2 permits");
Thread.sleep(1000);
} finally {
semaphore.release(2); // 2 permit burax
}
} else {
System.out.println("Could not get 2 permits");
}
}
}
Fair vs Unfair Semaphore
// Unfair semaphore (default)
Semaphore unfairSemaphore = new Semaphore(5);
// Fair semaphore - FIFO sırası
Semaphore fairSemaphore = new Semaphore(5, true);
class FairnessTest {
private final Semaphore semaphore;
public FairnessTest(boolean fair) {
this.semaphore = new Semaphore(1, fair);
}
public void work(String threadName) throws InterruptedException {
System.out.println(threadName + " is waiting...");
semaphore.acquire();
try {
System.out.println(threadName + " acquired permit");
Thread.sleep(100);
} finally {
System.out.println(threadName + " releasing permit");
semaphore.release();
}
}
}
Producer-Consumer Pattern
class BoundedBuffer<T> {
private final Queue<T> buffer = new LinkedList<>();
private final Semaphore items = new Semaphore(0); // Mövcud item sayı
private final Semaphore spaces; // Boş yer sayı
private final Object lock = new Object();
public BoundedBuffer(int capacity) {
this.spaces = new Semaphore(capacity);
}
public void put(T item) throws InterruptedException {
spaces.acquire(); // Boş yer gözlə
synchronized (lock) {
buffer.offer(item);
}
items.release(); // Yeni item signal
}
public T take() throws InterruptedException {
items.acquire(); // Item gözlə
synchronized (lock) {
T item = buffer.poll();
spaces.release(); // Boş yer signal
return item;
}
}
public int size() {
synchronized (lock) {
return buffer.size();
}
}
}
// İstifadə
BoundedBuffer<String> buffer = new BoundedBuffer<>(3);
// Producer
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
buffer.put("Item " + i);
System.out.println("Produced: Item " + i);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}).start();
// Consumer
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
String item = buffer.take();
System.out.println("Consumed: " + item);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}).start();
Rate Limiting
class RateLimiter {
private final Semaphore semaphore;
private final ScheduledExecutorService scheduler;
public RateLimiter(int permitsPerSecond) {
this.semaphore = new Semaphore(permitsPerSecond);
this.scheduler = Executors.newScheduledThreadPool(1);
// Hər saniyə permit-ləri restore et
scheduler.scheduleAtFixedRate(() -> {
int permitsToRelease = permitsPerSecond - semaphore.availablePermits();
if (permitsToRelease > 0) {
semaphore.release(permitsToRelease);
}
}, 1, 1, TimeUnit.SECONDS);
}
public boolean tryAcquire() {
return semaphore.tryAcquire();
}
public void acquire() throws InterruptedException {
semaphore.acquire();
}
public void shutdown() {
scheduler.shutdown();
}
}
// İstifadə - API rate limiting
RateLimiter rateLimiter = new RateLimiter(5); // 5 request per second
public void makeAPICall() throws InterruptedException {
rateLimiter.acquire();
try {
// API call
System.out.println("Making API call at " + new Date());
} finally {
// API call tamamlandı
}
}
Semaphore Monitoring
class MonitoredSemaphore {
private final Semaphore semaphore;
private final String name;
public MonitoredSemaphore(int permits, String name) {
this.semaphore = new Semaphore(permits);
this.name = name;
}
public void acquire() throws InterruptedException {
System.out.println(name + " - Available permits: " +
semaphore.availablePermits());
System.out.println(name + " - Queue length: " +
semaphore.getQueueLength());
semaphore.acquire();
System.out.println(name + " - Acquired permit. Remaining: " +
semaphore.availablePermits());
}
public void release() {
semaphore.release();
System.out.println(name + " - Released permit. Available: " +
semaphore.availablePermits());
}
public boolean hasQueuedThreads() {
return semaphore.hasQueuedThreads();
}
}
Binary Semaphore (Mutex)
class BinarySemaphore {
private final Semaphore mutex = new Semaphore(1);
private int sharedResource = 0;
public void criticalSection() throws InterruptedException {
mutex.acquire(); // Lock
try {
// Critical section
System.out.println(Thread.currentThread().getName() +
" accessing critical section");
sharedResource++;
Thread.sleep(1000);
System.out.println("Shared resource value: " + sharedResource);
} finally {
mutex.release(); // Unlock
}
}
}
Multiple Resource Types
class ResourceManager {
private final Semaphore printers = new Semaphore(3);
private final Semaphore scanners = new Semaphore(2);
private final Semaphore cameras = new Semaphore(1);
public void usePrinter() throws InterruptedException {
printers.acquire();
try {
System.out.println("Using printer...");
Thread.sleep(2000);
} finally {
printers.release();
}
}
public void useScanner() throws InterruptedException {
scanners.acquire();
try {
System.out.println("Using scanner...");
Thread.sleep(3000);
} finally {
scanners.release();
}
}
public void useMultipleResources() throws InterruptedException {
// Deadlock-dan qaçınmaq üçün eyni sıra ilə acquire et
printers.acquire();
try {
scanners.acquire();
try {
System.out.println("Using both printer and scanner");
Thread.sleep(1000);
} finally {
scanners.release();
}
} finally {
printers.release();
}
}
}
Best Practices
-
Always use try-finally:
semaphore.acquire();
try {
// Work with resource
} finally {
semaphore.release();
} -
Handle InterruptedException:
try {
semaphore.acquire();
// Work
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
} finally {
semaphore.release();
} -
Fair vs Unfair seçimi:
// High throughput üçün
Semaphore unfair = new Semaphore(permits);
// Fairness lazımdırsa
Semaphore fair = new Semaphore(permits, true); -
Monitoring əlavə edin:
System.out.println("Available permits: " + semaphore.availablePermits());
System.out.println("Queued threads: " + semaphore.getQueueLength());
Common Use Cases
- Database connection pools
- Thread pool sizing
- Rate limiting
- Resource management
- Download managers
- Print queue management
Semaphore vs Other Synchronizers
| Synchronizer | Purpose | Use Case |
|---|---|---|
| Semaphore | Permit-based access | Resource limiting |
| CountDownLatch | Wait for events | Startup coordination |
| CyclicBarrier | Thread meeting point | Parallel computations |
| ReentrantLock | Exclusive access | Critical sections |
Performance Considerations
- Fair semaphore - daha az throughput, lakin FIFO
- Unfair semaphore - yüksək throughput, lakin starvation riski
- tryAcquire() - blocking əvəzinə timeout istifadə edin
- Permit count - çox kiçik olmamalı (bottleneck), çox böyük olmamalı (resource waste)