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

  • Deadlock Starvation Livelock에서 붙잡아야 할 핵심 판단 기준은 무엇인가?
  • 멈춤의 원인도 여러 종류다라는 기준은 어떤 JVM/OS 신호로 확인되는가?
  • Java/Spring 코드에서 Deadlock Starvation Livelock 판단 기준은 어떤 장애 신호로 드러나는가?

개요

이 문서의 주제는 Deadlock Starvation Livelock이다. Deadlock, starvation, livelock의 차이와 thread dump에서 보이는 신호를 정리한다.

먼저 붙잡을 기준은 다음 문장이다. 멈춤의 원인도 여러 종류다. Deadlock은 서로의 lock을 기다려 멈추고, starvation은 특정 작업이 계속 밀리며, livelock은 바쁘게 움직이지만 유효한 진전이 없는 상태다.

핵심 모델

  • 판단 기준: 멈춤의 원인도 여러 종류다.
  • Deadlock은 서로가 가진 lock을 기다리는 순환 대기다.
  • Starvation은 전체 시스템은 움직이지만 특정 작업이 계속 실행 기회를 얻지 못하는 상태다.
  • Livelock은 retry와 상태 변경은 계속되지만 성공/완료량이 늘지 않는 상태다.
  • 세 문제는 모두 지연으로 보이지만 thread dump, queue 지연, retry/성공률 지표가 다르다.
  • 해결은 lock 순서 표준화, fairness/timeout, backoff, queue 분리처럼 원인별로 달라진다.

문서별 핵심 구분

  • deadlock은 서로의 lock을 기다려 진행이 멈춘 상태다.
  • starvation은 특정 작업이 계속 실행 기회를 얻지 못하는 상태다.
  • livelock은 움직이지만 유효한 진전이 없는 상태다.

원리

Deadlock은 A thread가 lock1을 잡고 lock2를 기다리는 동안 B thread가 lock2를 잡고 lock1을 기다리는 식의 순환 대기다. Thread dump에는 Found one Java-level deadlock이나 서로의 monitor를 기다리는 stack으로 나타난다.

Starvation은 deadlock처럼 완전히 멈춘 것은 아니지만 특정 요청이나 작업이 계속 실행 기회를 얻지 못하는 상태다. Unfair lock, 우선순위, 한쪽으로 쏠린 queue 소비, 긴 critical section이 원인이 될 수 있다.

Livelock은 thread가 계속 상태를 바꾸고 retry하지만 실제 처리는 전진하지 않는 상태다. 재시도 정책이 서로를 방해하거나 backoff 없이 충돌하면 CPU는 쓰는데 완료량이 늘지 않는다.

셋은 모두 “느리다”로 보일 수 있지만 증거가 다르다. Deadlock은 순환 대기, starvation은 특정 작업의 지속적 지연, livelock은 높은 활동량과 낮은 완료량으로 구분한다.

Java/Spring 연결

  • Spring 요청 thread가 서로 다른 lock을 다른 순서로 잡으면 deadlock이 날 수 있다.
  • @Transactional 내부의 DB row lock과 JVM lock을 섞으면 lock 순서가 더 복잡해진다.
  • Starvation은 특정 executor queue, unfair lock, rate limit, priority 정책에서 나타날 수 있다.
  • Livelock은 retry, optimistic lock 재시도, distributed lock 획득 실패 루프에서 발생할 수 있다.
  • Thread dump 반복 샘플과 처리 완료량 metric을 같이 봐야 “멈춤”과 “느린 전진”을 나눌 수 있다.
  • Spring 코드 리뷰에서는 Deadlock Starvation Livelock 판단 기준이 어떤 thread, pool, timeout, resource 설정으로 드러나는지 확인한다.

코드와 설정 예시

void transfer(Account a, Account b) {
    synchronized (a) {
        synchronized (b) {
            moveMoney(a, b);
        }
    }
}

두 thread가 transfer(a, b)transfer(b, a)를 동시에 실행하면 lock 획득 순서가 반대가 되어 deadlock 후보가 된다. 해결 기준은 lock을 없애는 것만이 아니라 항상 같은 순서로 lock을 잡도록 정렬하는 것이다.

관찰 명령

jcmd <pid> Thread.print > deadlock.txt
grep -n "Found one Java-level deadlock" deadlock.txt
grep -n "BLOCKED" deadlock.txt | head

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

지표 해석

신호먼저 의심할 것다음 확인
Found one Java-level deadlockmonitor deadlocklock graph, owner stack
특정 queue만 계속 지연starvationexecutor/lock fairness
retry 증가, 성공률 낮음livelockbackoff, conflict rate
CPU는 있는데 완료량 없음유효한 진전 없음throughput, retry loop

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

장애 상황에서 보는 순서

  1. Thread dump에서 Java-level deadlock 문구와 순환 대기를 확인한다.
  2. Deadlock이 없으면 특정 작업/queue가 계속 밀리는지 starvation을 본다.
  3. Retry, optimistic lock conflict, distributed lock 실패가 반복되는지 확인한다.
  4. CPU 사용과 완료량을 같이 보며 livelock 여부를 판단한다.
  5. Lock 순서, fairness, backoff, queue 분리 중 어떤 조치가 맞는지 고른다.

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

실전 팁

  • Deadlock 의심 시 thread dump를 한 번만 보지 말고 순환 대기가 유지되는지 다시 확인한다.
  • Starvation은 전체 처리량이 아니라 특정 작업/tenant/queue가 계속 밀리는지 본다.
  • Livelock은 retry 수, CPU, 완료량을 같이 봐야 “열심히 실패 중”인 상태가 보인다.
  • Lock 순서는 코드 리뷰에서 표준화하고, DB lock과 JVM lock을 섞는 구간은 별도 표시한다.
  • 장애 회고에는 deadlock graph나 starvation 대상 queue를 그림이나 표로 남긴다.

주니어 팁

  • Deadlock은 “서로 기다림”, starvation은 “계속 밀림”, livelock은 “움직이지만 완료 없음”으로 구분한다.
  • Thread dump에서 lock owner와 waiting thread를 같이 읽는다.
  • Retry가 많아질수록 좋아지는지, 오히려 완료량이 줄어드는지 확인한다.

시니어 팁

  • Lock 획득 순서는 코드베이스 전체에서 같은 기준으로 정한다.
  • Starvation 방지는 fairness 옵션보다 queue 설계, 작업 분리, timeout, backpressure가 먼저일 수 있다.
  • Retry 정책은 jitter, backoff, 최대 횟수, 관측 metric을 포함해야 livelock을 줄일 수 있다.

Guru급 팁

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

위험 신호!

  • Thread dump 없이 “deadlock”이라고 부른다.
  • Starvation을 평균 latency만 보고 놓친다.
  • Retry 폭증과 낮은 성공률을 보고도 livelock 가능성을 보지 않는다.
  • DB row lock과 JVM monitor lock의 순서를 함께 검토하지 않는다.
  • 재시작으로 deadlock을 풀고 lock graph를 남기지 않는다.

확인 질문

확인 질문

  • Deadlock을 thread dump에서 확인하려면 무엇을 봐야 하는가?
    • 서로가 보유한 lock을 상대가 기다리는 순환 대기와 lock owner stack을 확인해야 한다.
  • Starvation과 deadlock은 어떻게 다른가?
    • Deadlock은 순환 대기로 진행이 멈춘 상태이고, starvation은 시스템은 움직이지만 특정 작업이 계속 실행 기회를 얻지 못하는 상태다.
  • Livelock을 의심할 때 어떤 지표를 함께 봐야 하는가?
    • Retry 수, CPU 사용, 성공/완료량, 같은 실패 경로 반복 여부를 함께 봐야 한다.

참고 문서