데드락 ( 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
락을 획득하는 데 시간이 너무 오래 걸릴 경우, 프로그램이 대기 상태에 빠지지 않도록 하여
다른 작업을 수행할 수 있게 해줍니다.
'CS지식' 카테고리의 다른 글
| 스프링 핵심 개념 (4) | 2024.08.17 |
|---|---|
| [운영체제] 프로세스, 스레드 개념과 차이점 (3) | 2024.08.08 |
| [Java] 가비지 컬렉션 ( Garbage Collection ) 개념 및 동작 (1) | 2024.08.05 |
| [Java] 디자인패턴 (2) | 2024.08.04 |
| 클라우드 ( AWS ) 지식 (1) | 2024.08.02 |