💋 인트로
안녕하세요. 우아한테크코스 5기 깃짱이라고 합니다.
이번 포스팅에서는, 동기화의 필요성에 대해서 이야기하면서, 관련 용어들을 정리해보려고 합니다.
💋 동기화의 필요성
하나의 객체에 여러 개의 스레드가 접근한다면?
어떤 문제가 발생할 수 있을까?
✔️ 실습
예시를 통해 알아보자! (자세한 실습 코드는 이 링크 참고)
private static final class SynchronizedMethods {
private int sum = 0;
public void calculate() {
sum++;
}
}
calculate()
메서드를 동시에 1000개의 스레드가 호출한다고 해보자. 모든 스레드의 작업이 종료된 후 sum
의 값은 1000일까?
우울하게도 아니다.
왜지?
✔️ CPU의 입장에서 생각해보자!
sum++
은 우리가 보기엔 딱 한 줄이지만, CPU가 이 한 줄의 명령어를 어떻게 처리할 지에 대해서 생각해보면 쉽게 알 수 있다.
Assembly 언어로 아래 세 줄로 실행될 것이다.
LOAD sum to R1
R1 = R1 + 1
STORE R1 to sum
여기서 R1은 스레드 1의 레지스터, 즉 CPU 내부에 있는 기억 장치로서, 데이터를 일시적으로 저장하고 연산을 하기 위한 용도다. sum
은 Heap 영역(= 모든 스레드의 공통 메모리)에 저장된 변수다.
LOAD sum to R1
: sum 변수의 값을 R1 레지스터에 로드R1 = R1 + 1
: R1 레지스터의 값을 1 증가STORE R1 to sum
: R1 레지스터의 값을 sum 변수에 저장
정확히 하나의 스레드가 들어와서 모든 과정을 통과한 후에 그 다음 스레드가 들어와서 변화한 sum
을 가져와 실행하는 경우에만 결과값이 1000이 될 것이다.
최악의 경우에, 처음에 sum
값이 0일 때 모든 스레드가 진입하고,
LOAD sum to R1
이것만 실행한 후에 컨텍스트 스위칭 당해버린다면?
sum 값은 최종적으로 1이 될 수도 있다. 극단적이긴 하지만ㅋㅋㅋㅌㅌ
조회수, 좋아요 같은 곳에서 이런 일이 발생한다면 사실 알빠 X겠지만, 이게 잔고라면? 재고라면?
소송 당하지 않기 위해서 개발자는 ‘동기화’라는 것에 관심을 가져야 한다!
💋 어떻게 동기화 시킬까?
동기화라는 것도 사실 용어가 좀 낯설게 느껴지는데, 더 신기한 다른 단어들이 많다.
먼저 알고 넘어가자.
✔️ Race Condition (경쟁 조건)
여러 프로세스/스레드가 동시에
같은 데이터
를 조작할 때,
타이밍이나 접근 순서에 따라 결과가 달라질 수 있는 상황
✔️ Synchronization (동기화)
여러 프로세스/스레드를 동시에
실행해도, 공유 데이터의 일관성
을 유지하는 것
✔️ 컨텍스트 스위칭이 일어나지 않도록 하기 ⇒ 멀티 코어에서 무용지물
이 3가지 명령어를 실행하는 도중에 컨텍스트 스위칭이 일어나지 않도록 하면 된다.
LOAD sum to R1
R1 = R1 + 1
STORE R1 to sum
그치만, 이 방법은 싱글 코어에서만 가능하고 멀티 코어에서는 불가능하다.
⇒ 2개의 코어에서 2개의 스레드를 갖고 접근한다면, 컨텍스트 스위칭은 고려할 것도 없이, 그냥 각기 다른 두 스레드의 접근 타이밍에 따라서 이미 결과가 망가지게 된다.
✔️ 메서드를 1개의 스레드만 실행할 수 있도록 하기?!!
Lock을 사용해서 increment()
메서드를 실행할 때, 이 메서드를 딱 1개의 스레드만 실행할 수 있도록 하는 건 어떨까?
한 스레드가 실행중이라면, 다른 스레드는 기다리게 하는 방식이라면 멀티 코어에서도 문제될 게 없을 것 같다!
✔️ Critical Section (임계 영역)
공유 데이터의 일관성을 보장하기 위해 하나의 프로세스/스레드만 진입해서 실행 가능한 영역
💋 Critical Section Problem
임계 영역의 큰 틀은 아래와 같다.
do {
// enter section -> 임계 영역에 진입할 요건이 되는지 확인
// critical section
// exit section -> 이후에 다른 스레드도 임계 영역에 진입할 수 있도록 처리
} while (true)
근데 임계 영역에도 ‘문제’라는 게 있다. 아무렇게나 만들었다고 다 임계 영역이 되지 않는다는 뜻.
아래 세 가지 조건을 만족해야 critical section 문제의 해결책이 될 수 있다.
mutual exclusion
→ 상호 배제. 한 번에 하나의 스레드만 임계영역에서 실행할 수 있다.progress
→ 진행. 임계 영역이 비어있고, 여기 들어가고 싶은 스레드가 있다면 그중에 하나는 실행될 수 있어야 함. (진행이 계속 되어야 한다)bounded waiting
→ 한정된 대기. 임계 영역에 못 들어간 스레드도 무한정 기다리면 안된다.
💋 참고자료
- https://www.baeldung.com/java-concurrency
- https://engineerinsight.tistory.com/199
- https://www.baeldung.com/java-synchronized
- https://www.baeldung.com/java-synchronization-bad-practices
- https://www.youtube.com/watch?v=vp0Gckz3z64&list=PLcXyemr8ZeoQOtSUjwaer0VMJSMfa-9G-&index=4
도움이 되었다면, 공감/댓글을 달아주면 깃짱에게 큰 힘이 됩니다!🌟
비밀댓글과 메일을 통해 오는 개인적인 질문은 받지 않고 있습니다. 꼭 공개댓글로 남겨주세요!
'Computer Science > Operating System' 카테고리의 다른 글
[OS] 동기화 메커니즘(Synchronization Mechanisms)(2): 모니터(Monitor) (0) | 2023.11.21 |
---|---|
[OS] 동기화 메커니즘(Synchronization Mechanisms)(1): 스핀락(Spinlock), 뮤텍스(Mutex), 세마포어(Semaphore) (0) | 2023.11.20 |
[OS] CPU Bound VS IO Bound: 스레드는 몇 개가 좋을까? (0) | 2023.11.10 |
[OS] 컨텍스트 스위칭: 프로세스 컨텍스트 스위칭 VS 스레드 컨텍스트 스위칭 (0) | 2023.11.09 |
[OS] 프로세스/스레드의 개념: 멀티태스킹, 멀티스레딩, 멀티프로세싱, 멀티프로그래밍을 구분하자! (2) | 2023.11.09 |