[Java] Semaphore
Semaphore, java.util.concurrent.Semaphore, 세마포어, permit
정의
java.util.concurrent.Semaphore 는 N 개의 permit (허가) 을 발급/회수 하는 synchronizer. permit 이 있으면 진행, 없으면 Blocking 또는 즉시 실패.
자원 풀, rate limiting, 동시 접근 수 제한 같은 패턴의 표준 도구.
시각화
핵심 메서드
Semaphore s = new Semaphore(10); // 10 permit 발급
s.acquire(); // 1 permit 획득, 없으면 block
s.acquire(3); // 3 permit
s.tryAcquire(); // 즉시 시도, 실패 시 false
s.tryAcquire(timeout, unit);
s.release(); // permit 1 반환
s.release(3);
s.availablePermits(); // 남은 permit 수
가장 흔한 두 패턴
1. 동시 접근 제한 (concurrency limiter)
Semaphore limiter = new Semaphore(5); // 동시 5명만 접근
public void callExpensiveApi() throws InterruptedException {
limiter.acquire();
try {
externalApi.call();
} finally {
limiter.release();
}
}
외부 API 가 동시 5 요청까지만 허용한다면 이렇게 제한.
2. 자원 풀
class ConnectionPool {
private final Semaphore semaphore;
private final Queue<Connection> pool;
public ConnectionPool(int size) {
semaphore = new Semaphore(size);
pool = createPool(size);
}
public Connection acquire() throws InterruptedException {
semaphore.acquire();
return pool.poll();
}
public void release(Connection c) {
pool.offer(c);
semaphore.release();
}
}
JDBC connection pool, thread pool 같은 패턴의 핵심.
binary semaphore (= mutex 와 비슷)
Semaphore mutex = new Semaphore(1);
mutex.acquire();
try { ... }
finally { mutex.release(); }
ReentrantLock 과 비슷하지만 다른 점.
Semaphore는 누가 acquire 했는지 추적 안 함, A 가 acquire 하고 B 가 release 가능ReentrantLock은 reentrant, 같은 스레드 여러 번 acquire 가능
mutex 라면 ReentrantLock 또는 synchronized 가 일반적.
공정성 옵션
Semaphore fair = new Semaphore(5, true);
true 면 FIFO. 대기 중인 가장 오래된 스레드부터 acquire. false (기본) 면 어떤 스레드든 양보 시 가져갈 수 있음.
ReentrantLock 처럼 공정 모드는 throughput 이 낮다.
함정
1. release 빠뜨리면 permit 소실
limiter.acquire();
doWork(); // ❌ 예외 시 release 안 됨
limiter.release();
반드시 try/finally.
2. permit 수가 변할 수 있다
release() 가 acquire 보다 더 많이 호출되면 permit 수가 초기값보다 늘어난다 (의도적 사용 가능).
reducePermits(int) 로 줄일 수도 있다.
3. tryAcquire 의 다른 의미
tryAcquire() 는 즉시 false 반환, tryAcquire(timeout) 는 그 시간만큼 대기 후 false. 헷갈리기 쉬움.
참고
이 글의 용어 (4개)
- [Java] CountDownLatchjava
- 정의 는 한 스레드 또는 여러 스레드가 다른 스레드들의 작업 완료를 기다리는 일회용 synchronizer. 으로 초기화하고, 각 worker 가 작업 후 호출. waiter 는…
- [Java] CyclicBarrierjava
- 정의 는 N 개의 스레드가 모두 도착할 때까지 서로 기다리는 synchronizer. 모두 도착하면 동시에 출발, 다시 사용 가능 (cyclic). 시각화 핵심 메서드 가장 흔한…
- [Java] ReentrantLockjava
- 정의 는 키워드와 같은 상호 배제 (mutual exclusion) 를 제공하는 클래스 기반 락. JSR-166 (Java 5) 에서 추가됐다. "재진입 (reentrant)" …
- Blocking (블로킹)concurrency
- 정의 Blocking 은 호출이 결과가 준비될 때까지 호출 스레드를 멈춰두는 실행 방식. 멈춰 있는 동안 그 스레드는 다른 일을 할 수 없다. OS 가 스레드를 sleep 상태로…
💬 댓글