Ответ
Semaphore (Семафор) — это механизм синхронизации из пакета java.util.concurrent, который ограничивает количество потоков, которые могут одновременно получить доступ к общему ресурсу или пулу ресурсов. Он управляет набором виртуальных разрешений (permits).
Основные операции:
acquire()— Запрашивает одно разрешение. Если разрешения доступны, счетчик уменьшается, и поток продолжает работу. Если нет — поток блокируется до освобождения разрешения.release()— Освобождает одно разрешение, увеличивая счетчик и потенциально разблокируя ожидающий поток.
Пример: Ограничение доступа к пулу из 3-х соединений.
import java.util.concurrent.Semaphore;
public class ConnectionPool {
private final Semaphore semaphore = new Semaphore(3); // Макс. 3 потока одновременно
public void useResource() {
try {
semaphore.acquire(); // Получаем разрешение
// Критическая секция: работа с ограниченным ресурсом (например, сетевое соединение)
System.out.println(Thread.currentThread().getName() + " начал работу. Свободно разрешений: " + semaphore.availablePermits());
Thread.sleep(1000); // Имитация работы
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release(); // Всегда освобождаем разрешение в finally!
System.out.println(Thread.currentThread().getName() + " закончил работу.");
}
}
}
// Запуск 10 потоков, но одновременно работают не более 3-х.
Важные особенности:
- Честность (Fairness): Конструктор
Semaphore(int permits, boolean fair). Приfair=trueразрешения предоставляются ожидающим потокам в порядке FIFO, что предотвращает голодание, но может снижать производительность. - Множественное приобретение/освобождение: Методы
acquire(int permits)иrelease(int permits)работают с несколькими разрешениями сразу. - Попытка без блокировки:
tryAcquire()возвращаетfalseнемедленно, если разрешение недоступно.
Типичные сценарии использования:
- Ограничение числа одновременных соединений к базе данных или внешнему API.
- Реализация пулов ресурсов (например, потоков, соединений).
- Создание ограниченных по пропускной способности шлюзов (rate limiting).
Отличие от ReentrantLock: Семафор не привязан к конкретному потоку. Разрешение, полученное одним потоком, может быть освобождено другим (хотя это не является хорошей практикой).
Ответ 18+ 🔞
Так, слушай, вот эта штука — Semaphore, она же семафор, блядь. Представь себе, что у тебя есть, например, три ебучки на районе, а желающих — дохуя. Вот семафор — это такой суровый барин с палкой, который стоит и говорит: «Не-не-не, больше трёх не влезет, остальные ждите нахуй, пока кто-то не выйдет».
По сути, это просто счётчик разрешений из пакета java.util.concurrent. Разрешений (permits) — ограниченное количество. Хочешь зайти в «критическую секцию» — отдай разрешение. Выходишь — верни, чтобы следующий мог зайти.
Основные движухи:
acquire()— Это типа «дай мне пропуск, я готов». Если пропуск есть — забираешь и идёшь работать. Если нет — стоишь и тупишь, как мудак, пока кто-то не вылезет и не отдаст свой.release()— Это «всё, я отъебался, забирайте мой пропуск». После этого счётчик увеличивается, и какой-нибудь ждущий поток может, наконец, проснуться и зайти.
Вот смотри, как это выглядит в деле. Допустим, у нас пул из 3-х соединений, а потоков — 10.
import java.util.concurrent.Semaphore;
public class ConnectionPool {
private final Semaphore semaphore = new Semaphore(3); // Три разрешения, блядь. Больше — ни-ни.
public void useResource() {
try {
semaphore.acquire(); // Пытаемся урвать пропуск
// А вот тут мы уже внутри, работаем с ценным ресурсом
System.out.println(Thread.currentThread().getName() + " начал работу. Свободно пропусков: " + semaphore.availablePermits());
Thread.sleep(1000); // Делаем вид, что пашем
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release(); // ОБЯЗАТЕЛЬНО отдаём пропуск в finally! А то зависните все, ебанаты!
System.out.println(Thread.currentThread().getName() + " закончил работу.");
}
}
}
// Запускаешь 10 потоков, а нихуя — одновременно будут работать только 3. Остальные в очереди.
Ещё важные нюансы, блядь:
- Честность (Fairness): Можно создать семафор так:
Semaphore(int permits, boolean fair). Еслиfair=true, то разрешения будут раздавать по очереди, как в советской поликлинике — кто первый пришёл, тот первый и получил. Это честно, но медленнее. Еслиfalse— кто как успеет, может, и проскочишь. - Жадность: Можно сразу несколько разрешений брать или отдавать —
acquire(5),release(5). Например, если один ресурс требует для работы аж 5 «пропусков». - Попытка без унижения:
tryAcquire()— это типа «дай пропуск, но если нет — то и похуй, я не буду тут стоять, я пойду другим делом займусь». Возвращаетtrue/falseсразу.
Где это, блядь, применять?
- Ограничить количество одновременных запросов к базе, чтобы она не легла, как сука.
- Сделать пул чего-нибудь (соединений, потоков обработки).
- Rate limiting — чтобы не отправить на внешний API сто пятьсот запросов в секунду и не получить бан.
Чем не ReentrantLock? А тем, что семафор — он безликий. Пропуск взял один поток, а отдать может вообще другой, хотя так делать — моветон, конечно. А лок привязан к потоку: какой поток его взял, тот и должен отпустить.