Lock — 공유 자원 접근 제어를 위한 락 인터페이스
Lock — 공유 자원 접근 제어를 위한 락 인터페이스
Lock 구현은 synchronized 메서드·문보다 더 광범위한 락킹 연산을 제공해요. 더 유연한 구조화를 허용하고, 꽤 다른 속성을 가질 수 있으며, 여러 연관 Condition 객체를 지원할 수 있어요.
본문
개념 이해하기
락은 여러 스레드가 공유 자원에 접근하는 것을 제어하는 도구예요. 보통 독점 접근을 제공해요 — 한 번에 한 스레드만 락을 획득할 수 있고, 공유 자원에 대한 모든 접근이 먼저 락을 획득하길 요구해요. 다만 ReadWriteLock의 읽기 락처럼 동시 접근을 허용하는 락도 있어요.
public interface Lock
synchronized 메서드·문은 모든 객체와 연관된 암시적 모니터 락에 접근하지만, 블록 구조 방식으로만 획득·해제를 강제해요 — 여러 락은 역순으로 해제해야 하고, 획득한 것과 같은 어휘 범위에서 해제해야 해요.
Lock 인터페이스는 다른 범위에서 락을 획득·해제하고, 여러 락을 어떤 순서로든 획득·해제할 수 있게 해 "hand-over-hand"나 "chain locking" 같은 기법을 가능하게 해요.
늘어난 유연성에는 책임이 따릅니다. 블록 구조 락킹이 없으면 synchronized가 제공하는 자동 해제도 사라져요. 대부분의 경우 다음 관용구를 써야 해요:
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}
Lock은 synchronized보다 추가 기능을 제공해요 — 비블로킹 획득 시도(tryLock()), 인터럽트 가능 획득(lockInterruptibly()), 타임아웃 획득(tryLock(long, TimeUnit)).
메모리 동기화: 모든 Lock 구현은 내장 모니터 락이 제공하는 것과 같은 메모리 동기화 의미론을 강제해야 해요. 성공적인 lock은 성공적인 Lock 액션과, 성공적인 unlock은 성공적인 Unlock 액션과 같은 메모리 동기화 효과를 가져요.
메서드
void lock() — 락을 획득해요. 락을 사용할 수 없으면 획득할 때까지 현재 스레드가 대기 상태가 돼요.
void lockInterruptibly() throws InterruptedException — 현재 스레드가 인터럽트되지 않으면 락을 획득해요. 락을 사용할 수 없으면 획득하거나 인터럽트될 때까지 기다려요. 인터럽트되면 InterruptedException을 던지고 인터럽트 상태는 클리어돼요.
boolean tryLock() — 호출 시점에 락이 자유로울 때만 획득해요. 즉시 true/false를 반환. 전형적 관용구:
Lock lock = ...;
if (lock.tryLock()) {
try {
// manipulate protected state
} finally {
lock.unlock();
}
} else {
// perform alternative actions
}
boolean tryLock(long time, TimeUnit unit) throws InterruptedException — 주어진 대기 시간 내에 락이 자유로워지고 인터럽트되지 않으면 획득해요. 획득하면 true, 시간이 지나면 false. 시간이 0 이하면 전혀 기다리지 않아요.
void unlock() — 락을 해제해요. 보통 소유자만 해제할 수 있게 제한하며, 위반 시 (비검사) 예외를 던질 수 있어요.
Condition newCondition() — 이 Lock 인스턴스에 묶인 새 Condition 인스턴스를 반환해요. 조건에서 기다리기 전에 현재 스레드가 락을 보유해야 해요. Condition.await()는 기다리기 전에 락을 원자적으로 해제하고, 대기가 반환되기 전에 재획득해요.
UnsupportedOperationException— 이 구현이 조건을 지원하지 않을 때