Deadlock에 빠지지 않는 다양한 방법
- 식사하는 철학자 기준
1. 젓가락에 우선순위를 정하여 lock을 건다.
ChopStick cs1, cs2; if(cs1.order > cs2.order){ synchronized(cs1){ synchronized(cs2){ } } }else{ synchronized(cs2){ synchronized(cs1){ } } }
2. Interrupt가 가능한 Lock을 사용
ReentrantLock cs1, cs2; cs1.lockInterruptibly(); cs2.lockInterruptibly();
3. Timeout을 사용
ReentrantLock cs1,cs2; while(true){ cs1.lock(); try{ if(cs2.tryLock(1000, TimeUnit.MILLISECONDS)){ try{ ... } finally{ cs2.unlock(); } } else { ... } } finally { cs1.unlock(); } }
tryLock()은 데드락을 안걸리게 하는게 아니라, 데드락이 걸렸을 때 빠져나오게 하는 방법일 뿐이다.
모든 Thread에서 timeout을 발생시켰을 때, 곧바로 데드락에 빠지고 다시 풀리는 현상이 무한대로 반복하는 라이브락에 빠진다.
모든 Thread가 다른 timeout 시간을 가지면 이러한 현상이 적게 나타나지만, 데드락 발생 자체를 못하게 하는게 좋은 방법이다.
4. 협동 잠그기
- 더블 링크드 리스트에 노드를 삽입할 때, 특정 연속한 두 노드에 락을 걸고 그 사이에 노드를 삽입한다.
- 이러한 방법을 사용하면 여러 개의 스레드가 동시에 새 노드를 리스트에 더할 수 있고, 다른 동작들도 안전하게 수행할 수 있다.
5. 조건 변수
ReentrantLock lock; Condition condition = lock.newCondition(); Queue q; lock.lock(); try{ while(q.empty()){ condition.await(); /* 공유 자원 사용*/ } } finally { lock.unlock(); }
- 어떤 큐에 있는 값을 제거하고 싶을 때, 큐에 아무런 값이 없다면 큐가 적어도 하나의 데이터를 가질 때 까지 기다려야 한다.
- 코드가 더 복잡해 지지만, 동시성 측면에서는 더 낫다.
ReentrantReadWriteLock
- 여러 thread에서 하나의 자원을 동시에 read하는 것은 허용, read vs write, write vs read는 금지
- 다수의 read 동작 및 소수의 write 동작이 일어날 때 유용함
6. 원자 변수 - Atomic variable
일반 Int 형 변수를 사용할 때, 동기화를 까먹을 수 있다.
이를 방지하기 위해 AtomicInteger 클래스 변수를 이용한다.
Lock을 사용하지 않기 때문에, 데드락도 발생하지 않는다.
원자 변수는 논블로킹이나 락프리 알고리즘의 토대이다.
volatile 키워드(해당 변수를 읽거나 쓰는 동작에 대해 명령어 순서가 뒤바뀌지 않게 함) 보다는
,java.util.concurrent.atomic 클래스 중 하나를 사용하자.
AtomicIntegerFieldUpdater
- Atomic variable은 항상 atomic을 보장해야 하므로, 성능저하가 발생한다.
- 가끔 atomic을 보장해야할 때 AtomicIntegerFieldUpdater<T> 을 사용한다.
Ref) 7가지 동시성 모델
'컴퓨터공학 > Java Scala' 카테고리의 다른 글
스칼라 메모이제이션 구현 (0) | 2016.12.03 |
---|---|
Python glob.glob() to Scala (0) | 2016.11.24 |
Java vs C++ (0) | 2013.01.05 |
HotSpot (0) | 2013.01.04 |
Just-in-time (0) | 2013.01.04 |