Semaphore

Semaphore (카운팅 세마포어)

일정 개수의 허가(permit)를 유지하는 세마포어예요. acquire()는 허가를 얻을 수 있을 때까지 필요한 경우 블로킹하고, release()는 허가를 다시 추가해 블로킹된 취득자를 풀어줘요. 리소스에 접근할 수 있는 쓰레드 수를 제한하는 데 자주 쓰여요.

출처: Java API Reference

본문

개념적으로 Semaphore는 허가들의 집합을 유지해요. 실제 허가 객체는 사용되지 않고, 단지 사용 가능한 수의 개수만 세면서 동작해요.

예를 들어 항목 풀에 대한 접근을 세마포어로 제어하는 클래스는 이렇게 생겼어요.

class Pool {
    private static final int MAX_AVAILABLE = 100;
    private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);

    public Object getItem() throws InterruptedException {
        available.acquire();
        return getNextAvailableItem();
    }

    public void putItem(Object x) {
        if (markAsUnused(x)) available.release();
    }

    protected Object[] items = ...; // 관리 대상 항목들
    protected boolean[] used = new boolean[MAX_AVAILABLE];

    protected synchronized Object getNextAvailableItem() {
        for (int i = 0; i < MAX_AVAILABLE; ++i) {
            if (!used[i]) { used[i] = true; return items[i]; }
        }
        return null; // 도달하지 않음
    }

    protected synchronized boolean markAsUnused(Object item) {
        for (int i = 0; i < MAX_AVAILABLE; ++i) {
            if (item == items[i]) {
                if (used[i]) { used[i] = false; return true; }
                else return false;
            }
        }
        return false;
    }
}

항목을 얻기 전 각 쓰레드는 세마포어에서 허가를 획득해 항목을 사용할 수 있음을 보장받아요.

허가가 최대 1개까지만 유지되도록 초기화한 세마포어는 상호 배제 락으로 동작할 수 있어요. 이를 이진 세마포어라고 부르며, 대부분의 Lock 구현과 달리 소유자가 아닌 다른 쓰레드도 해제할 수 있다는 특성이 있어요.

생성자는 선택적으로 공정성(fairness) 파라미터를 받아요.

  • false일 때는 쓰레드가 허가를 획득하는 순서를 보장하지 않아요. 밀어내기(barging)가 허용되죠.
  • true일 때는 acquire 메서드들을 호출한 순서대로(FIFO) 허가를 받아요.
  • 시간 제한이 없는 tryAcquire는 공정성 설정을 따르지 않아요.
Semaphore s = new Semaphore(1, true);
if (s.tryAcquire()) {
    try { /* 임계 영역 */ } finally { s.release(); }
}

리소스 접근을 제어하는 세마포어는 스타베이션을 막기 위해 공정하게 초기화하는 게 일반적이에요.

acquire(3)처럼 한 번에 여러 허가를 취득·해제하는 편의 메서드도 있어요. 다만 이들은 선호 순서를 보장하지 않아요.

메모리 일관성 효과: 어떤 쓰레드에서 release() 같은 "해제" 메서드를 부르기 전의 동작은, 다른 쓰레드에서 acquire() 같은 "취득" 메서드가 성공한 뒤의 동작에 happen-before해요.

더 알아보기