본문 바로가기

CS지식

[Java] 데드락 ( DeadLock )

반응형

데드락 ( DeadLock, 교착상태) 이란?


컴퓨터 시스템에서 여러 프로세스나 스레드가 서로의 자원을 얻지못해 기다리며 무한정 대기 상태에 빠지는 상황

 

< 예시 >

프로세스 A는 자원 1을 가지고 자원 2 기다리고있고

프로세스 B는 자원 2을 가지고 자원 1를 기다리고 있다면

두프로세스는 무한하게 기다리게 되며, 이러한 상태를 데드락이라고 합니다.

 

< 데드락 발생 조건 4가지 >

1. 상호 배제 (Mutual Exclusion ) : 자원은 한 번에 하나의 프로세스만 사용할 수 있습니다.

 

 

2. 점유 대기( Hold and Wait ) : 자원을 이미 점유한 프로세스가 다른 자원을 추가로 요구하며, 그 자원이 할당될 때까지 대기합니다.

 

3. 비선점( No Preemption ) : 자원을 강제로 빼앗을 수 없습니다.

즉, 자원을 점유한 프로세스가 스스로 자원을 해제할 때까지 그 자원을 강제로 회수할 수 없습니다.

 

4. 순환 대기( Circular Wait ) : 프로세스들 간에 자원을 기다리는 관계가 순환적으로 형성되어 있습니다.

 

 

 

 

데드락 ( DeadLock ) 해결법


 

< 데드락 예방 ( Deadlock Prevention )  >

데드락 발생 조건 중 하나를 제거하여 데드락이 발생하지 않도록 하는 방법

 

 

1. 상호 배제 부정 : 가능한 자원을 공유 자원으로 전환합니다.

예를 들어, 읽기 전용 자원은 동시에 여러 프로세스가 사용할 수 있습니다.

 

2. 점유 대기 부정 : 프로세스가 자원을 점유하기 전필요한 모든 자원을 한 번에 요청하여 확보하도록 합니다.

필요한 모든 자원이 확보되지 않으면 프로세스는 자원을 점유하지 않고 대기합니다.

 

3. 비선점 조건 부정 : 자원을 점유하고 있는 프로세스가 추가 자원을 요청할 때,

필요한 자원을 확보할 수 없다면 이미 점유한 자원을 반납하도록 합니다.

 

4. 순환 대기 부정 : 자원에 대한 요청 순서를 정해주고, 각 프로세스가 그 순서( 고유번호 )에 따라 자원을 요청하도록 합니다.

 

 

< 데드락 회피 ( DeadLock Avoidance )  >

시스템이 자원 할당 상태를 검사하여 데드락이 발생하지 않을 경우에만 자원을 할당하는 방법

 

1. < 은행원 알고리즘 ( Banker's Algorithm ) >

  • 프로세스가 자원을 요구할 때,
    시스템은 자원을 할당한 후에도 안정 상태로 남아있게 되는지 사전에 검사하여 교착 상태 회피
  • 안정 상태면 자원 할당, 아니면 다른 프로세스들이 자원 해지까지 대기

 

 

2. < 자원 할당 그래프 알고리즘 ( Resource-Allocation Graph Algorithm ) > 

자원과 프로세스에 대해 요청 간선과 할당 간선을 적용하여 교착 상태를 회피하는 알고리즘

 

  • 프로세스가 자원을 요구 시 요청 간선을 할당 간선으로 변경 했을 시 사이클이 생성 되는지 확인
    • 자원에 하나의 인스턴스만 존재 시 교착 상태로 판별
    • 자원에 여러 인스턴스가 존재 시 교착 상태 가능성으로 판별
  • 사이클을 생성하지 않으면 자원을 할당한다.

 

 

데드락 탐지 ( Detection ) & 복구 ( Recovery )


교착 상태가 되도록 허용한 다음 회복시키는 방법 

 

 

< 탐지 ( Detection ) >

자원 할당 그래프를 통해 교착 상태를 탐지

자원 요청 시, 탐지 알고리즘을 실행시켜 그에 대한 오버헤드 발생함.

 

오버헤드 : 시스템 자원을 관리하거나 특정 작업을 수행하는 과정에서 발생하는 추가적인 시간, 자원 소비를 의미

 

< 회복 ( Recovery ) >

교착 상태를 일으킨 프로세스를 종료하거나, 할당된 자원을 해제시켜 회복하는 방법

 

1. 프로세스 종료 방법

  • 데드락에 관련된 모든 프로세스를 종료시켜 데드락을 해소
    • 확실하게 해결할 수 있지만, 종료된 프로세스가 다시 시작 및 작업 손실 유발함.

 

  • 데드락에 관련된 프로세스 중 하나씩 종료하면서 데드락이 해소되는지 확인합니다
    • 최소한의 작업 손실, 어떠한 프로세스를 종료해야 할지 결정이 어려움

 

2. 자원 선점 방법

  • 시스템에서 프로세스마다 우선순위가 매겨져 있는 경우, 우선순위가 낮은 프로세스의 자원을 먼저 선점
    • 우선순위가 높은 프로세스는 중요한 작업을 수행하고 있을 가능성이 높기 때문에

 

  • 진행 단계가 많이 진행되지 않은 프로세스는 다시 시작하더라도 손실이 크지 않을 수 있음.
    • 초기 단계에 있는 프로세스의 자원을 선점 ( 효율적 )

 

  • 자원을 선점당한 프로세스는 일시정지되고, 이후 자원이 다시 사용 가능해지면 프로세스를 재개
    • 다른 프로세스에게 필요한 자원을 제공하여 교착 상태 해소

 

 

 

Java - 데드락


Java에서는 여러 스레드가 동기화된 자원을 사용하는 과정에서 데드락이 발생할 수 있음.

 

예 )

" synchronized " 키워드를 사용한 블록이나 " ReentrantLock " 같은 Lock을 사용할 때, 여러 스레드가 서로의 락을

기다리며 데드락에 빠질수 있음.

 

 

< Java 데드락이 발생할 수 있는 예제 >

public class DeadlockExample {

    private final Object lock1 = new Object();
    private final Object lock2 = new Object();

    public void method1() {
        synchronized (lock1) {
            System.out.println("Thread 1: Holding lock 1...");

            try { Thread.sleep(100); } 
            catch (InterruptedException e) {}

            System.out.println("Thread 1: Waiting for lock 2...");

            synchronized (lock2) {
                System.out.println("Thread 1: Holding lock 1 & 2...");
            }
        }
    }

    public void method2() {
        synchronized (lock2) {
            System.out.println("Thread 2: Holding lock 2...");

            try { Thread.sleep(100); } 
            catch (InterruptedException e) {}

            System.out.println("Thread 2: Waiting for lock 1...");

            synchronized (lock1) {
                System.out.println("Thread 2: Holding lock 1 & 2...");
            }
        }
    }

    public static void main(String[] args) {
        DeadlockExample deadlockExample = new DeadlockExample();

        Thread thread1 = new Thread(deadlockExample::method1);
        Thread thread2 = new Thread(deadlockExample::method2);

        thread1.start();
        thread2.start();
    }
}

 

Thread 1은 lock1을 획득한 후 lock 2를 기다리고

Thread 2는 lock2을 획득한 후 lock 1 를 기다립니다.

이로 인해 데드락이 발생하여 두 스레드는 영원히 대기 상태에 빠지게 됨.

 

 

 

Java에서 데드락을 피하는 방법


< 1. 자원 획득 순서 지정 >

모든 스레드가 자원을 획득하는 순서를 동일하게 설정하여 순환 대기를 방지

예 )

A자원을 먼저, B자원을 다음으로 요청하도록 순서를 정해두면, 서로 다른 순서로 자원을 요청하는 데서 교착 상태를 예방

 

 

< 2. Lock Timeout  >

" ReentrantLock "의 " tryLock " 메서드를 사용하면 일정 시간 동안만 락을 시도하고, 실패시 다른 작업 수행

public class LockTimeoutExample {
    private final Lock lock = new ReentrantLock();

    public void performTask() {
        try {
            // 2초 동안 락을 시도하고, 실패하면 다른 작업 수행
            if (lock.tryLock(2, TimeUnit.SECONDS)) {
                try {
                    // 락을 획득한 경우 수행할 작업
                    System.out.println("Lock acquired, performing task...");
                    // 작업 수행
                } finally {
                    lock.unlock(); // 락 해제
                }
            } else {
                // 락 획득 실패 시 다른 작업 수행
                System.out.println("Could not acquire lock, doing something else...");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        LockTimeoutExample example = new LockTimeoutExample();
        example.performTask();
    }
}

 

 

< 코드 설명 >

tryLock(long time, TimeUnit unit)

주어진 시간 동안 락을 시도 하고, 시간이 초과되면 false를 반환하고, 락을 획득하면 true를 반환합니다.

 

 

Lock Timeout

락을 획득하는 데 시간이 너무 오래 걸릴 경우, 프로그램이 대기 상태에 빠지지 않도록 하여

다른 작업을 수행할 수 있게 해줍니다.

 

 

 

 

 

 

 

 

 

반응형