CH05 - Liveness
Deadlock: 서로 자원을 쥔 상태에서 서로의 자원이 release되기를 기다리는 상황
Liveness: 시스템이 계속 작동하기 위해 필요한 속성
Deadlock 사례
코드 해석
3명의 철학자가 스시를 먹는데, 젓가락이 3개(3쌍아님)가 있고 2개를 먼저 집어야 스시를 먹을 수 있다.
데드락이 걸릴 수 있는 상황인데, 여러번 돌려도 잘 돌아갈 수 도 있다. (어느 순간 서버가...)
sushiCount의 숫자를 500_000으로 크게 바꾸면,
1차: 작동안됨
2차: 작동안됨
3차: 10개 output 나오고 정지.
이 코드에서 데드락 발생하는 이유
만약 3명 철학자의 각 첫번째 젓가락인 chopstickA, chopstickB, chopstickC 가 동시에 lock을 걸게되면 데드락이 걸린다. 예를 들어, A는 B가 필요한데 다른 쓰레드에서 락을 걸어뒀고 B는 C가 필요한데 다른 쓰레드에서 락을 걸뒀고, C는 A가 필요한데 또 락이 걸려있으니, 데드락 상태가 된다.
/**
* Three philosophers, thinking and eating sushi
*/
import java.util.concurrent.locks.*;
class Philosopher extends Thread {
private Lock firstChopstick, secondChopstick;
private static int sushiCount = 5; //
public Philosopher(String name, Lock firstChopstick, Lock secondChopstick) {
this.setName(name);
this.firstChopstick = firstChopstick;
this.secondChopstick = secondChopstick;
}
public void run() {
while(sushiCount > 0) { // eat sushi until it's all gone
// pick up chopsticks
firstChopstick.lock();
secondChopstick.lock();
// take a piece of sushi
if (sushiCount > 0) {
sushiCount--;
System.out.println(this.getName() + " took a piece! Sushi remaining: " + sushiCount);
}
// put down chopsticks
secondChopstick.unlock();
firstChopstick.unlock();
}
}
}
public class DeadlockDemo {
public static void main(String[] args) {
Lock chopstickA = new ReentrantLock();
Lock chopstickB = new ReentrantLock();
Lock chopstickC = new ReentrantLock();
new Philosopher("Barron", chopstickA, chopstickB).start();
new Philosopher("Olivia", chopstickB, chopstickC).start();
new Philosopher("Steve", chopstickC, chopstickA).start();
}
}
// 1번째 결과
Steve took a piece! Sushi remaining: 4
Steve took a piece! Sushi remaining: 3
Steve took a piece! Sushi remaining: 2
Steve took a piece! Sushi remaining: 1
Steve took a piece! Sushi remaining: 0
// 2번째 결과
Olivia took a piece! Sushi remaining: 4
Olivia took a piece! Sushi remaining: 3
Olivia took a piece! Sushi remaining: 2
Barron took a piece! Sushi remaining: 1
Barron took a piece! Sushi remaining: 0해결방법
Steve의 젓가락의 순서를 chopstickC, chopstickA에서 chopstickA, chopstickC로 바꾼다.
그럼, Steve가 A를 기다리다리는 동안 올리비아의 B와 C를 통해 스시를 먹고 릴리즈를 하면, 배런의 A와 (릴리즈된) B를 가지고 스시를 먹고 릴리즈하면, 스티브도 (릴리즈된) A와 B를 가지고 밥을 먹을 수 있다.
즉, 데드락이 걸리지 않고 돌아가면서 처리하게 된다.
결과를 보면, 안멈추고 50만개 스시를 잘 처리하는 것을 볼 수 있다.
데드락 해결방법
Lock Ordering
모든 락들이 어느 쓰레드에서든 항상 동일한 순서로 발생되게 한다. 하지만 이 방법은 feasible 하진 않다.
Lock Timeoout
시간 제한
데드락과 비슷한 상황
한 쓰레드에서 에러가 나서, unlock을 못하는 상황
0으로 나누게 해서 에러 발생시키면, 다른 철학자들도 스시를 못먹는 상황이 발생한다.
해결방법
try catch -> 크리티컬 섹션은 항상 try 블록에 넣기!
Starvation
쓰레드가 자원을 계속 얻지 못하고 다른쓰레드에게 뺏기는 상황
아래 코드는 위의 코드와 동일한데, sushiEaten을 추가하여, 먹은 스시 개수를 확인한다.
셋다 먹긴 하지만, 공정하지 않다.
해결 방법
모두의 젓가락을 동일한 순서로 한다. (Lock Ordering)
결과를 본다면, 비슷한 숫자로 먹은 것을 볼 수 있다.
그런데 만약에 다음과 같이 for문으로 여러 쓰레드의 상황이 놓이면, 몇몇은 또 먹지 못하는 상황이 생긴다.
참 복잡하다...
Livelock
서로 자원 양보하는 상황
데드락을 피하기 위해서 만든 방안이 때로는 이 상황을 만들기도 한다.
아래코드가 완전히 livelock 상황은 아니지만, 흘끗 보여준다.
코드 실행하면, 끝나지가 않는다.
해결 방법은 랜덤 사용.
firstChopstick을 집기 전에 1~3초간 잠들어 있게 한다.
실행해보면, 철학자들이 스시가 0이 될때까지 잘 돌아가는 것을 확인할 수 있다.
Last updated