Семафор в Java

Semaphore хранит число разрешений. acquire() забирает разрешение или ждет, release() возвращает. Это удобно для ограничения параллельного доступа к внешнему ресурсу.

final class LimitedClient {
    private final Semaphore permits = new Semaphore(8, true);
 
    Response call(Request request) throws InterruptedException {
        permits.acquire();
        try {
            return remoteCall(request);
        } finally {
            permits.release();
        }
    }
}

finally не дает утечь разрешению. Но release() нужно выполнять только после успешного acquire():

boolean acquired = permits.tryAcquire(200, TimeUnit.MILLISECONDS);
if (!acquired) throw new TimeoutException();
try {
    useResource();
} finally {
    permits.release();
}

Важные свойства

  • семафор с N permits допускает до N участников;
  • он не отслеживает владельца: технически один поток может сделать release() за другой;
  • fairness true уменьшает обгон ожидающих, но может снизить throughput;
  • availablePermits() — диагностический снимок, не основание для «check then act»;
  • семафор ограничивает конкуренцию, но не делает сам ресурс потокобезопасным.

Для пула конкретных объектов одной семафоры мало: нужна еще потокобезопасная очередь ресурсов. Для одного владельца понятнее Mutex/Lock.

На собеседовании

  • Чем semaphore отличается от mutex? Считает N разрешений и не имеет строгого владельца.
  • Что произойдет при забытом release()? Разрешение утечет, система постепенно остановится.
  • Гарантирует ли fair semaphore строгий порядок во всех методах? Нет абсолютной гарантии для untimed tryAcquire.
  • Делает ли semaphore объект thread-safe? Нет, только ограничивает число одновременных пользователей.