멀티스레드와 동시성

Ch06. 동기화 - 임계 영역

webmaster 2024. 8. 10. 17:12
728x90

이런 문제가 발생한 근본 원인은 여러 스레드가 함께 사용하는 공유 자원을 여러 단계로 나누어 사용하기 때문이다.

  • 검증 단계: 잔액( balance )이 출금액( amount ) 보다 많은지 확인한다.
  • 출금 단계: 잔액( balance )을 출금액( amount ) 만큼 줄인다.
출금() {
	//검증 단계: 잔액(balance) 확인 
	//출금 단계: 잔액(balance) 감소
}

로직에는 하나의 가정이 있다.
스레드 하나의관점에서 출금()을보면검증 단계에서 확인한 잔액( balance ) 1000원은 출금 단계에서 계산을 끝마칠 때까지 같은 1000원으로 유지되어야 한다. 그래야 검증 단계에서 확인한 금액으로, 출금 단계에서 정확한 잔액을 계산할 있다.

그래야 검증 단계에서 확인한 1000원에 800원을 차감해서 200원이라는 잔액을 정확하게 계산할 있다. 결국 여기서는 내가 사용하는 값이 중간에 변경되지 않을 것이라는 가정이 있다.

그런데 만약 중간에 다른 스레드가 잔액의 값을 변경한다면, 큰 혼란이 발생한다. 1000원이라 생각한 잔액이 다른 값으로 변경되면 잔액이 전혀 다른 값으로 계산될 수 있다.

공유 자원

잔액( balance )은 여러 스레드가 함께 사용하는 공유 자원이다. 따라서 출금 로직을 수행하는 중간에 다른 스레드에서 이 값을 얼마든지 변경할 수 있다. 참고로 여기서는 출금() 메서드를 호출할 때만 잔액( balance )의 값이 변경된다. 따라서 다른 스레드가 출금 메서드를 호출하면서, 사용 중인 출금 값을 중간에 변경해 버릴 수 있다.

한 번에 하나의 스레드만 실행

만약 출금()이라는 메서드를 한 번에 하나의 스레드만 실행할 수 있게 제한한다면 어떻게 될까?
예를 들어 t1 , t2 스레드가 함께 출금()을 호출하면 t1 스레드가 먼저 처음부터 끝까지 출금() 메서드를 완료하고, 그다음에 t2 스레드가 처음부터 끝까지 출금() 메서드를 완료하는 것이다.

이렇게 하면 공유 자원인 balance를 한 번에 하나의 스레드만 변경할 수 있다. 따라서 계산 중간에 다른 스레드가 balance의 값을 변경하는 부분을 걱정하지 않아도 된다. (참고로 여기서는 출금() 메서드를 호출할 때만 잔액(balance)의 값이 변경된다.)

  • 더 자세히는 출금을 진행할 때 잔액( balance )을 검증하는 단계부터 잔액의 계산을 완료할 때까지 잔액의 값은 중간에 변하면 안 된다.
  • 이 검증과 계산 이 두 단계는 한 번에 하나의 스레드만 실행해야 한다. 그래야 잔액(balance )이 중간에 변하지 않고, 안전하게 계산을 수행할 수 있다.

임계 영역(critical section)

  • 여러 스레드가 동시에 접근하면 데이터 불일치나 예상치 못한 동작이 발생할 수 있는 위험하고 또 중요한 코드 부분을 뜻한다.
  • 여러 스레드가 동시에 접근해서는 안 되는 공유 자원을 접근하거나 수정하는 부분을 의미한다.
    • 예) 공유 변수나 공유 객체를 수정

앞서 우리가 살펴본 출금() 로직이 바로 임계 영역이다. 더 자세히는 출금을 진행할 때 잔액( balance )을 검증하는 단계부터 잔액의 계산을 완료할 때 까지가 임계 영역이다. 여기서 balance는 여러 스레드가 동시에 접근해서는 안 되는 공유 자원이다. 이런 임계 영역은 번에 하나의 스레드만 접근할 있도록 안전하게 보호해야 한다.
그럼 어떻게 번에 하나의 스레드만 접근할 있도록 임계 영역을 안전하게 보호할 있을까? 여러가지 방법이 있지만 자바는 synchronized 키워드를 통해 아주 간단하게 임계 영역을 보호할 있다

728x90