설명 순서는 다음과 같습니다.
1. Monitor 방법
2. Java에서 Monitor 방법
3. 예시를 보며 이해
4. Java에서 Lock & Condition 방법
1. Monitor 방법
- Java는 프로그래밍 레벨에서 동기화 문제 해결을 돕기 위해, Monitor 방법을 채택해서 제공
- Monitor의 전체적인 흐름 : Lock을 획득 -> 임계영역 진입 -> Lock반환. Lock을 획득하지 못할 경우 잠시 대기 상태에 들어가고, 누군가 Lock을 반환할 때 스레드를 깨우는 방법
- Monitor의 구조
① 임계영역 : 한 스레드만 들어가서 실행 가능한 영역
② Entry Set : Lock을 얻기위해 대기하는 영역
③ Wait Set : Lock을 얻었지만, 실행할 수 있는 조건에 맞지 않아 Lock을 반납하고 대기하는 영역
2. Java에서 Monitor 방법
- Java 객체는 내부적으로 하나의 Monitor를 가짐
- 동기화를 위한 키워드
① synchronized : 메서드 또는 블럭 앞에 붙임. 이 메서드 또는 블럭은 내부적으로 모니터 락을 획득해서, 상호배제를 보장함
② wait() : lock을 획득해서 synchronized 블럭에 들어왔는데 만약 어떤 해당 조건에 만족하지 않아서 일을 진행할 수 없을 때, 내가 갖고 있는 lock을 풀고 wait set에 들어감
③ notify() : wait set에 있는 thread 중 하나를 깨워서 entry set에 넣음
④ notifyAll() : wait set에 있는 모든 thread를 깨워서 entry set에 넣음
- 전체적인 흐름
* Lock획득(synchronized 블럭 진입) -> 임계영역 실행 -> Lock반환(synchronized 블럭 탈출)
* entry set에 있는 하나의 스레드를 빼서 synchronized 블럭에 진입
* synchronized 블럭에 들어갔더라도, 조건이 맞지 않은 경우 lock을 풀고 wait set에 들어가고 블럭 탈출
* 다른 스레드가 synchronized 실행 후 notify로 wait set에 있는 하나를 깨워서 entry set에 넣음
3. 예시를 보며 이해
- Producer 1번, 2번을 P1, P2로 하고, Consumer 1,2,3번을 C1, C2, C3라 하자
- 그림의 Buffer는 한번에 한 스레드만 사용해야 함
- 진행
① C1이 Buffer(Buffer의 Lock) 획득. 하지만, Buffer가 비어있으므로(실행할 수 없는 조건) C1은 wait set에 진입
② P1, P2, P3가 모두 Buffer를 획득하기를 원해서 P1만 Buffer 획득. 나머지는 entry set에 진입
③ P1은 Buffer하나를 채운 후 notify -> wait set에 있는 하나를 깨움 -> C1은 entry set에 진입
④ entry set에 있는 P2, P3, C1 중 하나가 Buffer 획득
⑤ ... 반복
- wait set중 어떤 것을 깨울 것인가? entry set중 어떤 것에 Lock을 줄 것인가? -> 이러한 문제는 starvation을 야기할 수 있음.
- 또한, wait set에 Producer와 Consumer가 같이 관리되기 때문에 만약 Producer가 Buffer를 가득 채우고 wait set에 있는 다른 Producer를 깨우면, 이 Producer는 Buffer를 획득하더라도 채울 수 없기 때문에 또 wait set에 들어감. Producer가 Buffer를 채웠다면, wait set에 있는 Consumer를 깨우는게 더 효율적인 상황임
4. Java에서 Lock & Condition 방법
- 위와 같은 문제를 해결하기 위해 Lock & Condition 방법을 제공함
- Monitor 방법과 다른 점은 Condition에 따라 wait set을 따로 구분하는 것임. 위 문제에서는 Producer wait set과 Consumer wait set을 따로 구분함
- 만약 P1이 Buffer를 채웠다면, Consumer wait set중 하나를 깨워서 entry set에 넣음
- 반대로 C1이 Buffer를 소비했다면, Producer wait set중 하나를 깨워서 entry set에 넣음
- 이렇게 스레드의 상태에 따라 종류를 나눠서 wait set을 만들 경우, 더 효율적인 상황이 됨
- 하지만, 같은 종류의 스레드(ex. P1, P2)끼리의 경쟁은 있기 때문에 기아현상이 발생할 가능성은 여전히 있고, Entry Set중 어떤 스레드에 lock을 줄 것인지에 대한 문제도 있기 때문에 기아현상 여전히 존재
- 공정하게 lock을 주기 위해서 java에서는 Lock lock = new ReentrantLock(boolean fair) 라는 공정성 매개변수를 제공하는데(true일 경우 오래 기다린 스레드에게 lock을 획득하게함), 그런데 그만큼 성능이 떨어지고, 대부분의 경우 공정함보다는 성능을 채택함
Java에서 Programming Level에서의 동기화 문제를 어떻게 해결하는지 알아볼 수 있었습니다.
'운영체제' 카테고리의 다른 글
시스템콜, 인터럽트 (0) | 2023.09.01 |
---|---|
동기, 비동기 (0) | 2023.08.31 |
동기화 (0) | 2023.08.11 |
페이지 교체 알고리즘 (0) | 2023.08.10 |
Deadlock (0) | 2023.08.08 |