동시성 제어를 위해 테스트 코드를 작성하던 와중 다른 문제 발생
구매에 성공하면 successfulPurchases 변수 int값을 ++ 해서
줄어든 한정수량이 얼마나 줄어들었는지 비교하는 테스트 코드를 작성중이였다
Convert to atomic..? 뭐죠
local variables referenced from a lambda expression must be final or effectively final successfulPurchases++;
심지어 빨간줄로 아얘 에러가 발생한다 이유가 뭘까?
일반 int 변수에 대해 ++ 연산을 사용하면 이 연산이 'atomic' 하지 않기 때문에 문제가 발생할 수 있다 여기서 atomic 하다는 말은 연산이 중단되지 않고 한 번에 완료 된다는 말
특히, int 변수에 대한 ++ 연산은 실제로는 '읽기', '증가', '쓰기'의 세 단계로 이루어지는데
자바의 메모리 구조는 위와 같이 CPU - RAM 아키텍처 기반으로 다음과 같이 동작한다.
1. 읽기(Read): Thread1은 메모리(RAM)에서 int 변수의 값을 읽어서 자신의 캐시(CPU Cache Memory 1)에 저장.
2. 증가(Increment): Thread1은 캐시에 있는 값에 1을 더함.
3. 쓰기(Write): Thread1은 새로운 값을 다시 RAM에 씀.
이 과정 중에 CPU 2의 core 1에서 동작하는 Thread1도 동일한 int 변수에 대해 같은 연산을 수행하려고 할 때 문제가 발생할 수 있다. CPU 1과 CPU 2의 캐시는 서로 독립적이기 때문에, CPU 1에서 값이 변경되어도 CPU 2의 캐시는 이를 바로 알지 못하고 오래된 값을 계속 사용할 수 있다. 결과적으로, 두 쓰레드가 거의 동시에 같은 변수를 변경하려고 하면 한 쓰레드의 변경 사항이 다른 쓰레드에 의해 덮어쓰여질 수 있다.
지겹도록 만난 개념아닌가? Rase Condition
AtomicInteger는 내부적으로 이런 문제를 방지하기 위해 설계된 클래스이다 incrementAndGet() 같은 메서드를 사용하면, 해당 연산이 atomic하게 수행되도록 보장할 수 있다고 한다. 따라서 멀티쓰레드 환경에서 안전하게 정수 값을 증가시키려면 AtomicInteger를 사용하자!