← 전체 강의로 돌아가기

04 / RESOURCE CONFLICT

멈춘 것과
헛도는 것

데드락은 자원을 잡은 채 서로 기다리고, 라이브락은 자원을 양보하며 계속 움직인다. 두 현상을 프로세스 상태와 자원 흐름으로 구분해 보자.

ONE QUESTION

프로세스는 지금 기다리는가, 재시도하는가?

자원을 누가 가지고 있는지와 프로세스가 어떤 상태인지 함께 보면 멈춤과 헛돎을 구분할 수 있다.

데드락 · Deadlock “네가 놓을 때까지 나도 못 가.”
라이브락 · Livelock “내가 양보할게. 어? 다시 부딪혔네.”

30초 핵심

CPU 사용률만 보지 말고 프로세스 상태와 자원 점유·해제 흐름을 함께 본다.

데드락은 자원을 가진 채 서로 기다려 Blocked / Waiting으로 멈춘 상태이고, 라이브락은 양보·재시도를 반복하며 Running 중이지만 실제 진전이 없는 상태다.

QUESTION → CHANGE → EXPLANATION

두 프로세스는 자원을 기다리는가, 아니면 다시 시도하는가?

자원 1과 자원 2를 두 프로세스가 나누어 쓰는 상황을 한 단계씩 관찰한다. 각 카드에서 상태와 자원 목록을 함께 읽어 보자.

  1. 질문자원 때문에 멈춘 것인가?
  2. 조작다음 단계·자동 재생을 눌러 본다.
  3. 변화점유·대기·해제를 비교한다.
  4. 해설상태·CPU·진전을 따로 판단한다.

자동 재생은 흐름을 보여 주고, 일시정지는 한 단계의 자원 상태를 읽을 시간을 준다.

자원 충돌 시뮬레이션

같은 출발점에서 데드락과 라이브락이 어떻게 갈라지는지 비교한다.

1 / 4

현재 단계를 확인해 보세요.

다음 단계를 눌러 상태와 자원 흐름의 차이를 관찰해 보세요.

  1. 하나씩 먼저 얻는다

    데드락으로 이어질 수 있는 흐름

    아직 멈추지는 않았지만, 각자 가진 자원을 놓지 않는 선택이 시작됐다.

    프로세스 P1
    Running
    • 자원 1 · 점유
    • 자원 2 · 필요
    프로세스 P2
    Running
    • 자원 2 · 점유
    • 자원 1 · 필요

    라이브락으로 이어질 수 있는 흐름

    같은 출발점이다. 다음 단계에서 실패하면 잠금을 놓고 다시 시도하는 규칙을 사용한다.

    프로세스 P1
    Running
    • 자원 1 · 점유
    • 자원 2 · tryLock
    프로세스 P2
    Running
    • 자원 2 · 점유
    • 자원 1 · tryLock
  2. 두 번째 자원에서 충돌한다

    데드락 · 서로의 자원을 기다림

    잠금을 기다리므로 두 프로세스의 실행 흐름이 멈춘다.

    프로세스 P1
    Blocked / Waiting
    • 자원 1 · 계속 점유
    • 자원 2 · P2가 점유
    프로세스 P2
    Blocked / Waiting
    • 자원 2 · 계속 점유
    • 자원 1 · P1이 점유
    상태대기 조건이 풀리지 않음
    CPU해당 스레드는 낮은 편

    라이브락 · 서로의 자원을 양보함

    두 번째 잠금을 기다리지 않고 첫 번째 잠금을 놓을 준비를 한다.

    프로세스 P1
    Running
    • 자원 1 · 곧 해제
    • 자원 2 · 획득 실패
    프로세스 P2
    Running
    • 자원 2 · 곧 해제
    • 자원 1 · 획득 실패
    상태실패 처리와 재시도 실행
    CPU바쁜 재시도라면 높을 수 있음
  3. 시간이 지나도 진전이 없다

    데드락 · 멈춰 있는 고리

    자원 1과 2의 점유자가 바뀌지 않아 대기 고리가 유지된다.

    프로세스 P1
    Blocked / Waiting
    • 자원 1 · 점유 유지
    • 자원 2 · 계속 대기
    프로세스 P2
    Blocked / Waiting
    • 자원 2 · 점유 유지
    • 자원 1 · 계속 대기
    진전작업 완료 0건
    관찰대기 관계가 변하지 않음

    라이브락 · 움직이는 고리

    둘 다 해제하고, 같은 타이밍에 같은 패턴으로 다시 자원을 잡는다.

    프로세스 P1
    Running
    • 자원 1 · 해제
    • 자원 1 · 재획득 시도
    프로세스 P2
    Running
    • 자원 2 · 해제
    • 자원 2 · 재획득 시도
    진전작업 완료 0건
    관찰해제·재획득 로그는 계속 생김
  4. 해결 규칙을 넣어 진전을 만든다

    데드락 해결 · 대기 고리를 끊기

    모든 작업이 자원 1 → 자원 2 순서로만 잠그면 서로 반대 순서로 기다릴 수 없다.

    프로세스 P1
    Running
    • 자원 1 → 자원 2
    • 작업 후 모두 해제
    프로세스 P2
    Ready
    • 같은 순서로 대기
    • P1 후 실행

    라이브락 해결 · 재시도 타이밍 바꾸기

    실패한 작업이 서로 다른 시간만큼 쉬면 다음 시도가 겹치지 않을 가능성이 커진다.

    프로세스 P1
    Running
    • 자원 1·2 · 획득
    • 작업 완료
    프로세스 P2
    Ready
    • Random Backoff
    • 다음 차례에 성공
지금 관찰할 것 데드락은 자원 점유와 대기가 고정되고, 라이브락은 자원 변화와 실행 로그가 계속 생긴다. 두 경우 모두 “실제 작업 완료”가 있는지 확인해야 한다.
  • 자원 점유·필요
  • 대기·충돌
  • 해제·진전

상태와 자원, 두 축으로 구분하기

“멈췄다”와 “계속 움직인다”는 표현을 프로세스 상태와 자원 관계로 번역하면 두 문제가 선명해진다.

데드락 · Deadlock

프로세스들이 자원을 점유한 채 서로가 가진 자원을 기다린다. 잠금이 풀릴 조건이 사라져 Blocked / Waiting 상태에서 깨어나지 못한다.

라이브락 · Livelock

프로세스들이 충돌을 피하려고 자원을 놓고 다시 시도한다. Running 동작은 이어지지만 유용한 작업의 완료라는 진전은 없다.

관찰 기준데드락라이브락
프로세스 상태Blocked / WaitingRunning 또는 실행 가능한 Ready
자원 흐름하나를 가진 채 다른 자원을 기다림얻었다가 놓고 다시 얻기를 반복
CPU 단서해당 대기 스레드는 낮은 편바쁜 재시도라면 높은 편
진전대기 관계가 고정되어 없음상태 변화는 있지만 실제 완료는 없음

판단 순서: 현재 상태 → 누가 어떤 자원을 점유했는가 → 다음 행동은 대기인가 재시도인가 → 실제 작업이 완료되었는가.

데드락: 자원을 가진 채 서로 기다린다

골목길에서 두 차가 마주 보고 멈춘 것처럼, 각 프로세스가 양보하지 않고 상대방의 자원을 기다리는 상태다.

프로세스 A자원 A를 가지고
자원 B를 기다림
프로세스 B자원 B를 가지고
자원 A를 기다림
-- 설명용 의사 SQL: WAIT는 특정 DBMS의 표준 문법이 아님
-- Transaction A
BEGIN;
UPDATE table_a SET value = value + 1 WHERE id = 1; -- 자원 A 잠금
UPDATE table_b SET value = value + 1 WHERE id = 1; -- 자원 B 대기

-- Transaction B
BEGIN;
UPDATE table_b SET value = value + 1 WHERE id = 1; -- 자원 B 잠금
UPDATE table_a SET value = value + 1 WHERE id = 1; -- 자원 A 대기

DBMS가 뭐예요?

DBMS(Database Management System)는 데이터를 저장·검색·수정하고, 트랜잭션과 잠금 같은 동시 접근 규칙을 관리하는 소프트웨어다. PostgreSQL·MySQL·Oracle Database가 예다.

발생하면 어떻게 하나요?

많은 DBMS는 대기 관계를 감지해 한 트랜잭션을 취소하거나 롤백한다. 그래도 애플리케이션은 취소된 작업을 재시도할지, 오류를 어떻게 보여 줄지 결정해야 한다.

라이브락: 계속 움직이지만 앞으로 가지 못한다

복도에서 서로 비켜 주려다 같은 방향으로 움직이는 것처럼, 실패 처리와 재시도가 계속되지만 실제 작업은 끝나지 않는다.

function work(first, second) {
  while (true) {
    if (!tryLock(first)) continue;       // 기다리지 않고 바로 실패

    if (!tryLock(second)) {
      unlock(first);                     // 먼저 잡은 자원을 양보
      continue;                          // 곧바로 다시 시도
    }

    doUsefulWork();
    unlock(second);
    unlock(first);
    return;
  }
}

// A: work(lock1, lock2)
// B: work(lock2, lock1)
헷갈리기 쉬운 생각: “계속 움직이니 진행 중이다.”

Running 로그나 자원 해제 로그가 생겨도 결과 데이터가 바뀌거나 작업이 완료되지 않으면 진전이 아니다.

더 정확한 생각: “행동은 있지만 유용한 진전은 없다.”

tryLock 실패 → 해제 → 즉시 재시도가 같은 타이밍으로 반복되는지 살펴본다.

CPU 주의: 쉬지 않고 재시도하면 CPU 사용이 높을 수 있지만, 재시도 사이에 sleep하거나 CPU를 양보하면 낮을 수도 있다. CPU는 보조 단서다.

데드락의 네 가지 조건 · Coffman Conditions

보통 아래 네 조건이 모두 성립해야 데드락이 가능해진다. 하나를 깨면 데드락을 예방할 여지가 생긴다.

1. 상호 배제

한 번에 한 프로세스만 사용할 수 있는 자원이 있다.

2. 점유하며 대기

이미 가진 자원을 놓지 않은 채 다른 자원을 기다린다.

3. 비선점

다른 프로세스가 자원을 강제로 빼앗을 수 없고, 가진 쪽이 직접 놓아야 한다.

4. 순환 대기

A는 B의 자원을, B는 A의 자원을 기다리는 고리가 생긴다.

예방 아이디어: 모든 작업이 자원 1 → 자원 2처럼 정해진 순서로 잠금을 얻으면 서로 반대 순서로 기다리는 순환 대기를 만들기 어려워진다.

식사하는 철학자와 실제 라이브락

공유 자원을 여러 작업이 나누어 쓰는 상황에서는 데드락과 라이브락이 코드·네트워크·분산 시스템에서 다른 모습으로 나타난다.

식사하는 철학자 문제

철학자 다섯 명이 양옆 포크 두 개를 모두 가져야 식사할 수 있다. 모두 왼쪽 포크를 먼저 집으면 각자 하나씩 가진 채 오른쪽 포크를 기다리므로 데드락이 된다.

각 철학자: 왼쪽 포크 점유 → 오른쪽 포크 대기
결과: 아무도 두 포크를 모두 얻지 못함

실제 라이브락 사례

동시 자료구조의 compare-and-swap 재시도, 잠금 충돌 후 즉시 재시도, 충돌한 네트워크 장치의 동시 재전송, 여러 서비스의 같은 시점 재시도에서 생길 수 있다.

실패 감지 → 즉시 재시도 → 다시 충돌
      ↖ 같은 타이밍으로 반복 ↙

해결 방법은 무엇이 다를까?

데드락은 대기 고리를 끊고, 라이브락은 반복 타이밍과 무한 재시도를 끊는 방향으로 접근한다.

데드락 해결

자원 순서 정렬: 항상 같은 순서로 잠근다.
감지 후 취소: 한 트랜잭션을 롤백해 고리를 끊는다.
타임아웃: 영원히 기다리지 않고 포기한다.
보유 시간 줄이기: 잠금 중 긴 계산·네트워크 요청을 피한다.

라이브락 해결

Random Backoff: 실패 후 임의의 시간만큼 쉰다.
재시도 제한: 무한 while (true)를 막는다.
공정한 순서: 대기열로 다음 차례를 보장한다.
진전 기록: 반복과 성공을 구분한다.

내가 설명해 보기

아래 질문에 답할 때 “상태 → 자원 → 다음 행동 → 진전” 순서를 사용해 보자.

  1. 데드락과 라이브락의 차이를 프로세스 상태와 자원 흐름으로 설명해 보자.
  2. A는 자원 1을, B는 자원 2를 가진 채 서로 기다릴 때 왜 데드락인지 말해 보자.
  3. tryLock 실패 후 잠금을 놓고 곧바로 재시도하는 코드가 라이브락이 될 수 있는 이유를 설명해 보자.
  4. DBMS가 하는 일을 데이터 저장, 트랜잭션, 잠금 중 두 가지 이상을 사용해 설명해 보자.
  5. Random Backoff가 라이브락을 줄이는 원리를 “같은 타이밍”과 연결해 설명해 보자.

이해 확인 퀴즈

정답을 보기 전에 먼저 자신의 말로 답해 보자.

  1. 객관식: 다음 중 데드락에 가장 가까운 상황은 무엇일까?
    A. 두 스레드가 실패할 때마다 잠금을 놓고 즉시 다시 시도한다.
    B. A는 잠금 1을 가진 채 잠금 2를 기다리고, B는 잠금 2를 가진 채 잠금 1을 기다린다.
    C. 한 스레드가 자원을 얻지 못해 한 번 오류를 반환하고 종료한다.
    D. 작업이 끝난 뒤 잠금을 해제하고 다음 작업을 시작한다.
  2. 참/거짓: 라이브락은 항상 CPU 사용률이 100%이므로 CPU 사용률만 측정하면 확실히 판별할 수 있다.
  3. 짧은 설명: DBMS가 무엇이며, 트랜잭션 사이의 데드락을 감지한 뒤 할 수 있는 일을 한 가지 적어 보자.
  4. 빈칸 채우기: 이미 가진 자원을 놓지 않은 채 다른 자원을 기다리는 Coffman 조건은 무엇일까?
  5. 상황 판단: 두 작업의 로그에 “lock 1 성공 → lock 2 실패 → lock 1 해제 → 즉시 재시도”가 수천 번 반복된다. 두 작업 모두 Running이고 결과 데이터는 바뀌지 않았다. 무엇에 가까운지와 해결 방법 하나를 적어 보자.
정답과 해설 보기

1. B. 각 스레드가 자원을 하나씩 점유한 채 상대방의 자원을 기다리므로 순환 대기가 생긴다. A는 잠금을 놓고 즉시 다시 시도하므로 라이브락에 가깝다.

2. 거짓. 쉬지 않는 재시도라면 CPU가 높을 수 있지만, 재시도 사이에 잠들거나 CPU를 양보하면 낮을 수도 있다. 프로세스 상태와 자원 흐름을 먼저 봐야 한다.

3. DBMS는 데이터를 저장·검색·수정하고 트랜잭션·잠금 같은 동시 접근 규칙을 관리하는 소프트웨어다. 데드락을 감지하면 한 트랜잭션을 취소·롤백해 대기 고리를 끊을 수 있다.

4. 점유하며 대기(Hold and Wait). 자원을 가진 채 다른 자원을 기다리는 조건이다.

5. 라이브락. 코드가 계속 실행되지만 자원을 얻었다 놓는 행동만 반복하고 실제 결과가 진행되지 않는다. Random Backoff, 재시도 횟수 제한, 공정한 대기열 등을 사용할 수 있다.

한 문장으로 기억하기

데드락은 자원을 가진 채 서로 기다려 멈춘 상태이고, 라이브락은 자원을 양보하며 계속 실행하지만 진전이 없는 상태이므로, CPU보다 프로세스 상태와 자원 점유·해제 흐름을 먼저 보자.