Gymterview
middle

Что такое Semaphore?

Semaphore — это синхронизатор из пакета java.util.concurrent, реализующий классический счётный семафор. Он управляет доступом к ограниченному числу ресурсов через внутренний счётчик разрешений (permits).

Принцип работы:

  • При создании задаётся начальное количество разрешений.
  • acquire() — поток запрашивает разрешение. Если счётчик > 0, он уменьшается на 1 и поток проходит. Если счётчик = 0, поток блокируется до появления свободного разрешения.
  • release() — поток возвращает разрешение, увеличивая счётчик на 1. Если есть ожидающие потоки, один из них разблокируется.

Пример — ограничение одновременных подключений к БД:

Код: пул подключений через Semaphore
class ConnectionPool {
    private final Semaphore semaphore;
    private final BlockingQueue<Connection> pool;

    ConnectionPool(int maxConnections) {
        this.semaphore = new Semaphore(maxConnections, true); // fair
        this.pool = new LinkedBlockingQueue<>();
        for (int i = 0; i < maxConnections; i++) {
            pool.add(createConnection());
        }
    }

    Connection acquire() throws InterruptedException {
        semaphore.acquire(); // Ждём, если все разрешения заняты
        return pool.poll();
    }

    void release(Connection conn) {
        pool.offer(conn);
        semaphore.release(); // Возвращаем разрешение
    }
}

Основные методы:

Метод Описание
acquire() Захватить одно разрешение (блокирующий)
acquire(int n) Захватить n разрешений
tryAcquire() Попытаться захватить без блокировки
tryAcquire(time, unit) Попытаться с таймаутом
release() Освободить одно разрешение
availablePermits() Текущее количество свободных разрешений
drainPermits() Забрать все доступные разрешения

Fair vs Unfair. Конструктор new Semaphore(permits, true) создаёт «честный» семафор — потоки получают разрешения в порядке очереди (FIFO). По умолчанию семафор «нечестный» — это быстрее, но потоки могут получить разрешение вне очереди.

Бинарный семафор (permits = 1) — по поведению похож на мьютекс, но с важным отличием: семафор не имеет понятия «владелец» — release() может вызвать любой поток, не только тот, кто вызвал acquire(). Это отличает его от ReentrantLock, где unlock() может вызвать только поток-владелец.

Аналогия: семафор — это парковка с ограниченным числом мест. На въезде стоит счётчик свободных мест. Когда машина заезжает — счётчик уменьшается. Когда выезжает — увеличивается. Если мест нет (счётчик = 0), новые машины ждут у шлагбаума.

На собеседовании могут спросить: «Чем Semaphore с одним permit отличается от ReentrantLock?» Ключевые отличия: семафор не реентрабельный (повторный acquire() из того же потока заблокирует его), и release() может вызвать любой поток.