* Critical Section = Critical Region (임계구역)
여러 스레드가 같은 메모리에 접근하여 값을 변경할 때 의도된 대로
동작하지 않는 문제가 발생한다.
이렇게 여러 스레드가 동시에 접근했을 때 문제가 발생하는 코드 부분을
"Critical Section" 또는 "Critical Region" 이라 부른다.
[0이 할당되는 과정, "Critical Section"이 발생되는 과정]
예를 들어, 두 개의 스레드가 동시에 add 메서드에 접근한다고 가정해보자.
첫 번째 스레드가 values 배열에 값을 할당하기 전에 CPU 제어권을 잃어버리고,
두 번째 스레드가 접근하여 값을 할당한다.
그리고 첫 번째 스레드가 다시 CPU 제어권을 얻게 되면
이미 두 번째 스레드에 의해 할당된 값이 있으므로
원래 할당되어야 할 값은 덮어쓰여지게 된다.
이로 인해 values 배열에 원하는 값 대신 0이 할당되는 결과가 나타날 수 있다.
즉, 값이 읽기 전에 다른 스레드에 의해 덮어쓰여질 수 있기 때문에 발생하는 문제이다.
이러한 동시성 문제를 해결하기 위해서는 상호 배제(Mutual Exclusion) 메커니즘을 사용하여
Critical Section에 대한 동시 접근을 제한해야한다.
[동시성 문제는 순차적으로 발생하지 않을 수도 있다.]
여러 스레드가 동시에 실행되는 환경에서는 실행 순서가 보장되지 않으므로,
값이 할당되는 순서는 예측할 수 없다.
따라서, 첫 번째 값이 두 번째 값에 할당되고,
두 번째 값이 이미 할당된 값에 의해 0으로 처리된 후 세 번째 값이 할당되는 것이 일반적이다.
하지만 동시성 문제에서는 실행 순서가 보장되지 않기 때문에,
두 번째 값에 세 번째 값이 할당될 수도 있다.
이는 스레드 스케줄링에 따라 다양한 실행 결과가 나타날 수 있다는 의미이다.
* Critical Section 해결 ? Synchronized ?
Critical Section에 오직 한 개의 스레드만 접근하게 하면
비동기로 인한 문제가 발생하지 않는다.
=> 즉 동기화로 처리한다.
=> 동기화?
- 여러 스레드가 동시에 실행하는 것이 아니고
여러 스레드가 순차적으로 접근하는 것.
단 순차적으로 실행한다는 것은 동시 실행의 이점을 버리는 것이기 때문에
스레드를 사용하기 전의 상태와 같다.
기존의 실행 방식 처럼 실행 시간이 많이 걸린다.
다음 메서드를 동기화 처리해 보자.
=> synchronized
- 크리티컬 섹션 구간에 이 키워드를 붙이면 오직 한 번에 한 개의 스레드만이 접근할 수 있다.
- 먼저 접근한 스레드가 나가야만 다음 스레드가 진입할 수 있다.
- 즉 크리티컬 섹션을 뮤텍스 구간으로 설정한다.
* Synchronized 문제해결 ? 비동기 & 동기 코드블록을 지정
비동기 코드 블록 (print1 메서드):
여러 스레드가 동시에 진입할 수 있다.
여러 스레드가 동시에 작업을 수행하므로, 작업이 병렬적으로 실행된다.
실행 순서가 보장되지 않으므로, 스레드 간 상호작용이나 의존성이 없는 작업에 적합하다.
동기 실행 메서드 (print2 메서드):
한 번에 한 스레드만 진입할 수 있다.
스레드 간에 실행 순서가 보장되며, 작업이 순차적으로 실행된다.
Critical Section에 대한 동시 접근 문제를 해결할 수 있다.
공유 자원에 대한 안전한 접근을 보장할 수 있다.
* wait()/notify()
* 44. 스레드 재사용하기 : GOF의 Flywight 사용
sleep()과 wait() 메서드는 모두 스레드의 실행을 일시적으로 멈추게 하는 역할을 한다.
그러나 주요한 차이점은 다음과 같다.
sleep(): 주어진 시간 동안 스레드의 실행을 일시적으로 멈추고,
다른 스레드에게 CPU 사용 권한을 양보한다.
이는 일시적인 지연이 필요한 상황에서 사용되며, 동기화와는 관련이 없다.
wait(): 스레드가 다른 스레드의 실행 신호를 기다리는 상태로 전환된다.
wait() 메서드는 해당 객체의 락을 해제하고, 다른 스레드에게 실행 기회를 양보한다.
notify() 또는 notifyAll() 메서드를 통해 깨어나 실행을 재개한다.
wait() 메서드는 동기화된 블록 내에서 호출되어야 하며, synchronized 키워드와 함께 사용된다.
따라서, sleep()은 주어진 시간 동안 스레드를 멈추고 CPU 사용 권한을 양보하는 방식이며,
wait()은 다른 스레드의 실행 신호를 기다리면서 실행을 멈추고 다른 스레드에게 락을 양보하는 방식이다.