Əsas məzmuna keçin

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

  1. Always use try-finally:

    semaphore.acquire();
    try {
    // Work with resource
    } finally {
    semaphore.release();
    }
  2. Handle InterruptedException:

    try {
    semaphore.acquire();
    // Work
    } catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    return;
    } finally {
    semaphore.release();
    }
  3. Fair vs Unfair seçimi:

    // High throughput üçün
    Semaphore unfair = new Semaphore(permits);

    // Fairness lazımdırsa
    Semaphore fair = new Semaphore(permits, true);
  4. 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

SynchronizerPurposeUse Case
SemaphorePermit-based accessResource limiting
CountDownLatchWait for eventsStartup coordination
CyclicBarrierThread meeting pointParallel computations
ReentrantLockExclusive accessCritical 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)