Для чего используется Semaphore в Java?

«Для чего используется Semaphore в Java?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Semaphore (семафор) — это синхронизатор, который ограничивает количество потоков, одновременно имеющих доступ к общему ресурсу или пулу ресурсов.

Основные сценарии использования:

  1. Ограничение пула соединений: Например, не более 10 одновременных подключений к базе данных или внешнему API.
  2. Управление доступом к аппаратным ресурсам: Ограничение числа потоков, использующих принтер или сетевое устройство.
  3. Реализация паттерна "Producer-Consumer" с ограниченным буфером.

Как это работает? Семафор инициализируется количеством "разрешений" (permits). Поток должен захватить (acquire()) разрешение перед доступом к ресурсу и освободить (release()) его после завершения работы.

Пример: Ограничение параллельных HTTP-запросов

import java.util.concurrent.Semaphore;

public class RateLimiter {
    private final Semaphore semaphore = new Semaphore(3); // Макс 3 параллельных запроса

    public void makeHttpRequest(String url) {
        try {
            semaphore.acquire(); // Ждем, если уже 3 потока работают
            System.out.println(Thread.currentThread().getName() + " делает запрос к " + url);
            // Имитация HTTP-запроса
            Thread.sleep(1000 + (int)(Math.random() * 2000));
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            semaphore.release(); // Освобождаем слот для другого потока
            System.out.println(Thread.currentThread().getName() + " завершил запрос.");
        }
    }

    public static void main(String[] args) {
        RateLimiter limiter = new RateLimiter();
        // Запускаем 10 потоков, но одновременно будут работать не более 3
        for (int i = 0; i < 10; i++) {
            new Thread(() -> limiter.makeHttpRequest("api.example.com/data")).start();
        }
    }
}

Ключевые методы: acquire(), release(), tryAcquire() (неблокирующий), availablePermits().