ReentrantLock — 재진입 가능한 상호 배제 락

ReentrantLock — 재진입 가능한 상호 배제 락

ReentrantLocksynchronized 메서드·문으로 접근하는 암시적 모니터 락과 같은 기본 동작·의미론을 가진 재진입(reentrant) 상호 배제 락이지만, 확장된 기능을 제공해요. 자바에서 명시적 락을 쓸 때 가장 대표적인 클래스예요.

출처: Java API Reference

본문

개념 이해하기

ReentrantLocksynchronized와 같은 기본 동작·의미론을 가지되 확장된 기능을 가진 재진입 상호 배제 락이에요.

public class ReentrantLock
extends Object
implements Lock, Serializable
  • ReentrantLock마지막으로 성공적으로 락을 걸었지만 아직 해제하지 않은 스레드에 의해 소유돼요. lock을 호출한 스레드는 락이 다른 스레드가 소유하지 않으면 성공적으로 획득해 반환해요. 현재 스레드가 이미 락을 소유하면 즉시 반환해요 (재진입). 이는 isHeldByCurrentThread(), getHoldCount()로 확인할 수 있어요.
  • 공정성(fairness) 매개변수: true로 설정하면 경합 시 가장 오래 기다린 스레드에게 접근을 우선 부여해요. 아니면 특정 접근 순서를 보장하지 않아요. 공정한 락은 많은 스레드가 접근하는 프로그램에서 전체 처리량이 낮을 수 있지만, 획득 시간의 분산이 작고 기아(starvation)가 없음을 보장해요. 참고로 락의 공정성이 스레드 스케줄링의 공정성을 보장하진 않아요.
  • 시간 없는 tryLock()은 공정성 설정을 존중하지 않아요 — 다른 스레드가 기다려도 락이 사용 가능하면 성공해요.
  • lock 호출 직후 try 블록을 항상 사용하는 게 권장 관행이에요:
class X {
    private final ReentrantLock lock = new ReentrantLock();
    public void m() {
        lock.lock();       // block until condition holds
        try {
            // ... method body
        } finally {
            lock.unlock();
        }
    }
}
  • 직렬화 시 역직렬화된 락은 직렬화 당시 상태와 무관하게 잠금 해제 상태예요.
  • 같은 스레드가 최대 2147483647번의 재귀 락을 지원해요. 초과 시 락킹 메서드가 Error를 던져요.

생성자

public ReentrantLock()ReentrantLock(false)와 동등한 인스턴스를 만들어요.

public ReentrantLock(boolean fair) — 주어진 공정성 정책을 가진 인스턴스를 만들어요.

  • fairtrue면 공정한 순서 정책 사용

Lock 메서드

public void lock() — 락을 획득해요. 다른 스레드가 안 잡고 있으면 즉시 획득하고 홀드 카운트를 1로 설정. 현재 스레드가 잡고 있으면 홀드 카운트 증가. 다른 스레드가 잡고 있으면 획득할 때까지 대기.

public void lockInterruptibly() throws InterruptedException — 현재 스레드가 인터럽트되지 않으면 획득해요. 명시적 인터럽트 지점이므로 인터럽트에 정상/재진입 획득보다 우선 응답해요.

  • InterruptedException

public boolean tryLock() — 호출 시점에 다른 스레드가 안 잡고 있을 때만 획득해요. 공정 락이라도 사용 가능하면 즉시 획득해요("barging"). 공정성을 존중하려면 tryLock(0, TimeUnit.SECONDS) 사용. 이미 홀드 중이면 카운트 증가 후 true.

public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException — 주어진 대기 시간 내에 획득. 공정 락이면 다른 스레드가 기다리는 중에는 사용 가능해도 획득하지 않아요. NullPointerException, InterruptedException.

public void unlock() — 홀드 카운트를 감소시켜요. 0이 되면 락을 해제해요. 현재 스레드가 홀더가 아니면 IllegalMonitorStateException.

public Condition newCondition() — 이 락과 함께 쓸 Condition 인스턴스를 반환해요. 대기 중 인터럽트되면 wait가 종료되고 InterruptedException. 대기 스레드는 FIFO 순서로 신호를 받아요.

상태 조회 메서드

public int getHoldCount() — 현재 스레드의 락 홀드 수를 조회해요. 주로 테스트·디버깅용.

public boolean isHeldByCurrentThread() — 현재 스레드가 락을 보유하고 있으면 true.

public boolean isLocked() — 어떤 스레드든 락을 보유하고 있으면 true. 시스템 상태 모니터링용이지 동기화 제어용이 아니에요.

public final boolean isFair() — 공정성이 설정됐으면 true.

protected Thread getOwner() — 현재 소유한 스레드 또는 null. 더 광범위한 모니터링 하위클래스를 만드는 데 도움. best-effort 근사치.

public final boolean hasQueuedThreads() — 이 락을 획득하려 기다리는 스레드가 있는지 조회해요. 시스템 상태 모니터링용.

public final boolean hasQueuedThread(Thread thread) — 주어진 스레드가 락을 기다리며 큐에 있는지. NullPointerException.

public final int getQueueLength() — 락을 기다리는 스레드 수의 추정치.

protected Collection<Thread> getQueuedThreads() — 락을 기다릴 수 있는 스레드 컬렉션 (best-effort).

public boolean hasWaiters(Condition condition) — 주어진 조건에서 기다리는 스레드가 있는지. IllegalMonitorStateException, IllegalArgumentException, NullPointerException.

public int getWaitQueueLength(Condition condition) — 조건에서 기다리는 스레드 수의 추정치(상한).

protected Collection<Thread> getWaitingThreads(Condition condition) — 조건에서 기다리는 스레드 컬렉션 (best-effort).

public String toString() — 락과 상태("Unlocked" 또는 "Locked by" + 소유 스레드 이름)를 식별하는 문자열.

더 알아보기 (Learn more)