자바 언어·플랫폼 용어 사전
동시성synchronized · ReentrantLock · 데드락

임계 구역을 한 번에 하나만 지나가게 하는 장치. synchronized는 자동 해제, ReentrantLock은 기능이 많다.

여러 스레드가 동시에 지나가면 안 되는 구간(임계 구역) 을 한 번에 하나만 통과시키는 장치.

synchronized

synchronized void inc() { count++; }              // 인스턴스(this) 를 잠근다
synchronized static void sinc() { s++; }          // 클래스 객체를 잠근다
void f() { synchronized (lock) { ... } }          // 특정 객체를 잠근다
  • 블록을 벗어나면 예외가 나도 자동으로 풀린다
  • 재진입 가능 — 같은 스레드가 이미 쥔 락을 다시 잡을 수 있다
  • 가시성도 함께 보장한다 (unlock → lock 의 happens-before)

ReentrantLock

private final ReentrantLock lock = new ReentrantLock();

void f() {
    lock.lock();
    try { ... } finally { lock.unlock(); }        // finally 를 빠뜨리면 영원히 안 풀린다
}

// synchronized 로는 못 하는 것들
if (lock.tryLock(1, TimeUnit.SECONDS)) { ... }    // 시간 제한을 두고 시도
lock.lockInterruptibly();                          // 대기 중 인터럽트 가능
new ReentrantLock(true);                           // 공정 모드 (먼저 온 순서대로)
Condition cond = lock.newCondition();              // 조건별 대기열을 여러 개

무엇을 잠글 것인가

// ❌ 문자열 리터럴 — 상수 풀에서 공유된다. 무관한 코드와 락이 겹친다
synchronized ("lock") { }

// ❌ 박싱된 값 — 캐시 범위에서 같은 객체다
synchronized (Integer.valueOf(1)) { }

// ❌ 바뀌는 참조 — 잠근 대상이 도중에 달라진다
synchronized (this.field) { this.field = new Object(); }

// ✅ 전용 final 객체
private final Object lock = new Object();

락 범위는 좁게

// ❌ 외부 호출을 락 안에 둔다 — 그동안 아무도 못 들어온다
synchronized void f() { httpClient.call(); count++; }

// ✅ 필요한 부분만
void f() {
    var result = httpClient.call();
    synchronized (lock) { count++; }
}

데드락과 그 처방

T1: A 잠금 → B 대기

  • T2: B 잠금 → A 대기 — → 순환 대기 = 데드락

  • ① 항상 같은 순서로 잠근다 (가장 효과가 크다)

  • ② 락을 쥔 채 다른 락을 요구하지 않는다

  • ③ tryLock 에 타임아웃을 걸어 포기할 수 있게 한다

jstack <pid>     # "Found one Java-level deadlock" 이 찍힌다

Java 24 이후의 변화

예전에는 가상 스레드에서 synchronized 가 캐리어를 고정(pinning)했다

  • 그래서 ReentrantLock 으로 바꾸라는 조언이 있었다

Java 24 의 JEP 491 이 이를 제거했다

  • 이제 pinning 회피 목적으로 바꿀 필요는 없다
  • tryLock · Condition 같은 기능이 필요할 때만 ReentrantLock 을 쓴다

면접 함정

  • "ReentrantLock이 항상 낫다"unlock을 빠뜨릴 위험이 있다. 기능이 필요 없으면 synchronized가 안전하다.
  • "synchronized는 무겁다" → 경합이 없으면 JVM이 최적화한다. 경합이 문제이지 키워드가 문제가 아니다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 동시성