이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • synchronized Lock ReentrantLock에서 붙잡아야 할 핵심 판단 기준은 무엇인가?
  • lock은 보호 범위와 대기 정책이다라는 기준은 어떤 JVM/OS 신호로 확인되는가?
  • Java/Spring 코드에서 synchronized Lock ReentrantLock 판단 기준은 어떤 장애 신호로 드러나는가?

개요

이 문서의 주제는 synchronized Lock ReentrantLock이다. synchronized, Lock, ReentrantLock의 차이와 Spring 서버에서의 사용 기준을 정리한다.

먼저 붙잡을 기준은 다음 문장이다. lock은 보호 범위와 대기 정책이다. synchronizedReentrantLock의 차이는 문법보다 lock을 기다리는 방식, 중단 가능성, timeout, fairness, 관찰 가능성에서 드러난다.

핵심 모델

  • 판단 기준: lock은 보호 범위와 대기 정책이다.
  • synchronized는 monitor 기반 lock이며 thread dump에서 BLOCKED와 owner를 확인하기 쉽다.
  • ReentrantLocktryLock, timeout, interrupt, fairness 같은 대기 정책을 코드로 표현할 수 있다.
  • Lock 도구 선택보다 보호 범위와 lock 안의 작업이 더 중요하다.
  • Lock 안의 외부 I/O는 DB pool, HTTP client pool, request thread 고갈을 함께 만들 수 있다.
  • Lock 경합은 CPU보다 latency, BLOCKED/WAITING thread, lock owner stack으로 판단한다.

문서별 핵심 구분

  • synchronized는 monitor 기반 mutual exclusion을 제공한다.
  • ReentrantLock은 timeout, interrupt, fairness 같은 선택지를 제공한다.
  • lock 선택보다 lock 범위와 외부 I/O 포함 여부가 더 중요할 때가 많다.

원리

synchronized는 monitor 기반 mutual exclusion을 제공한다. 문법이 단순하고 JVM이 monitor owner와 BLOCKED thread를 thread dump에 잘 보여 준다.

ReentrantLocktryLock, timeout, interruptible lock, fairness 같은 선택지를 제공한다. 대신 unlock()finally에서 보장하지 않으면 lock이 풀리지 않는 위험이 있다.

Lock의 핵심은 어떤 상태를 보호하느냐와 lock을 잡은 동안 무엇을 하느냐다. Lock 안에서 DB, 외부 HTTP, 파일 I/O를 호출하면 안전성보다 대기열과 장애 전파 비용이 더 커질 수 있다.

Lock 경합은 CPU가 낮아도 latency가 높고 thread가 BLOCKED/WAITING에 몰리는 형태로 보인다. 따라서 평균 latency보다 p95/p99와 thread dump 반복 샘플이 중요하다.

Java/Spring 연결

  • Spring service method에 lock을 걸면 해당 bean을 통과하는 모든 요청 thread가 영향을 받는다.
  • Lock 안에서 repository나 WebClient를 호출하면 DB pool, HTTP client pool, thread pool 대기가 서로 묶인다.
  • synchronized 경합은 thread dump의 BLOCKED와 monitor owner로 추적하기 쉽다.
  • ReentrantLock은 timeout/interrupt 정책을 코드에 명시할 수 있어 장애 중 무한 대기를 줄일 수 있다.
  • 여러 서버 인스턴스가 같은 데이터를 다루면 JVM lock 대신 DB lock, optimistic lock, distributed lock을 검토해야 한다.
  • Spring 코드 리뷰에서는 synchronized Lock ReentrantLock 판단 기준이 어떤 thread, pool, timeout, resource 설정으로 드러나는지 확인한다.

코드와 설정 예시

private final ReentrantLock lock = new ReentrantLock();
 
public void change() {
    if (lock.tryLock(100, TimeUnit.MILLISECONDS)) {
        try {
            updateSharedStateOnly();
        } finally {
            lock.unlock();
        }
    } else {
        throw new BusyException();
    }
}

예시는 ReentrantLock을 선택했다면 대기 시간과 실패 정책을 코드로 표현할 수 있음을 보여 준다. 중요한 것은 lock 안에 공유 상태 변경만 두고 외부 I/O를 넣지 않는 것이다.

관찰 명령

jcmd <pid> Thread.print | grep -A5 BLOCKED
top -H -p <pid>
jcmd <pid> JFR.start name=lock duration=60s filename=lock.jfr

synchronized Lock ReentrantLock 관찰 명령은 결론이 아니라 가설 확인 도구다. 운영 중에는 반복 간격, 대상 PID, 수집 시각을 함께 남기고 출력이 큰 명령은 범위를 제한한다.

지표 해석

신호먼저 의심할 것다음 확인
BLOCKED thread 증가synchronized monitor 경합lock owner stack
WAITING/parking 증가Lock/condition 대기ReentrantLock 사용 구간
CPU 낮고 p99 지연lock wait로 request thread 정체반복 thread dump
Lock owner가 외부 I/O 중critical section 과다DB/HTTP stack

synchronized Lock ReentrantLock에서는 핵심 신호와 보조 지표를 같은 시간축에서 맞춘다. 먼저 변한 신호와 뒤따라 포화된 신호를 분리해야 원인과 결과를 구분할 수 있다.

장애 상황에서 보는 순서

  1. Thread dump에서 BLOCKED, WAITING, parking thread 수를 확인한다.
  2. Lock owner stack이 어떤 코드와 외부 자원을 잡고 있는지 본다.
  3. Lock 안에 DB, HTTP, file I/O, sleep, retry가 있는지 확인한다.
  4. Timeout이 필요한 경로인지, 무한 대기가 허용되는 경로인지 나눈다.
  5. JVM lock으로 충분한지, DB/분산 lock이 필요한지 배포 구조로 판단한다.

이 순서는 synchronized Lock ReentrantLock 후보를 빠르게 좁히기 위한 기본 루틴이다. 결론을 내리기 전에는 애플리케이션 증거와 JVM/OS 증거를 최소 한 번 이상 맞춘다.

실전 팁

  • Lock을 잡은 구간에는 공유 상태 변경만 남기고 외부 I/O는 밖으로 뺀다.
  • synchronized는 단순하고 dump 해석이 쉽지만 timeout 정책을 표현하기 어렵다.
  • ReentrantLock은 선택지가 많은 만큼 finally unlock과 실패 정책이 반드시 필요하다.
  • Lock 경합은 평균 latency가 아니라 p95/p99와 BLOCKED thread 수로 본다.
  • 장애 회고에는 lock owner stack과 대기 thread stack을 함께 남긴다.

주니어 팁

  • Lock을 쓰기 전에 어떤 공유 상태를 보호하는지 먼저 적는다.
  • ReentrantLock을 쓰면 unlock()이 반드시 finally에 있는지 확인한다.
  • Lock 안에 DB나 외부 HTTP 호출이 있으면 병목 후보로 표시한다.

시니어 팁

  • Lock 선택 기준은 기능보다 실패 정책이다. 무한 대기, timeout, interrupt, fairness 중 무엇이 필요한지 정한다.
  • Lock 구간의 최대 실행 시간을 SLO와 thread pool 크기 기준으로 제한한다.
  • JVM 내부 lock이 전체 서비스 정합성을 보장하지 못하는 경우 DB나 분산 coordination으로 책임을 옮긴다.

Guru급 팁

  • synchronized Lock ReentrantLock 주제는 단일 snapshot보다 변화 방향이 중요하다. 같은 명령을 간격을 두고 실행해 상태가 증가, 유지, 회복 중 어디인지 본다.
  • 고급 도구는 synchronized Lock ReentrantLock 가설을 좁힐 때만 사용하고 기본 지표와 모순되는지 먼저 확인한다.
  • synchronized Lock ReentrantLock 조치 후에는 CPU, memory, socket, disk, pool 중 다음 포화 지점이 어디로 이동했는지 확인한다.

위험 신호!

  • Lock 안에서 외부 I/O를 수행해 다른 pool 장애까지 함께 묶는다.
  • ReentrantLock에서 예외 경로 unlock()을 보장하지 않는다.
  • Timeout 없는 lock 대기를 사용자 요청 경로에 둔다.
  • Thread dump 없이 “lock이 문제 같다”는 추측으로 코드만 바꾼다.
  • 단일 JVM lock으로 다중 인스턴스 정합성을 해결했다고 착각한다.

확인 질문

확인 질문

  • synchronizedReentrantLock을 선택할 때 문법보다 먼저 봐야 할 것은 무엇인가?
    • 보호할 공유 상태, 대기 정책, timeout/interrupt 필요성, lock 범위와 외부 I/O 포함 여부를 봐야 한다.
  • Lock 경합을 운영에서 확인하려면 어떤 증거가 필요한가?
    • Thread dump의 BLOCKED/WAITING thread, lock owner stack, latency p95/p99, 반복 샘플이 필요하다.
  • Lock 안에 DB 호출을 넣으면 위험한 이유는 무엇인가?
    • DB 지연이 lock 대기와 request thread 고갈로 전파되어 작은 지연이 전체 처리량 저하로 커질 수 있기 때문이다.

참고 문서