이전에 동기화 관련 공부를 하면서 자바에선 Atomic 자료형들이 thread-safe한 작업을 보장해준다는 것과, 하드웨어 레벨 instruction으로 test_and_set()
, compare_and_swap()
같은 것들이 있다는 것을 배웠다. 이번에 간단하게 AtomicInteger
를 사용해 getAndIncrement()
해야하는 상황이 있었는데, 실제로 이 자료형의 메소드들이 어떻게 작동하는지 궁금해 찾아봤다.
compare_and_swap()
메커니즘을 사용해 원자성을 보장한다 // compare_and_swap()의 pseudo code
function compare_and_swap(p: pointer to int, old: int, new: int) is
if *p ≠ old
return false
*p ← new
return true
compare_and_swap()
이 어떤 로직으로 작동하는지는 이해했지만, 어떻게 hw instruction level에서 atomic하게 작동하는지는 이해 못했다 @_@ 캐쉬가 어쩌고 하던데 나중에 다시 찾아봐야겠다
원자성을 만족하는 compare_and_swap()
덕분에 경쟁 상태에서도 문제 없이 하나의 스레드만 increment가 가능하다
아래는 AtomicInteger 클래스로 가면 볼 수 있는 코드이다.
public final int getAndIncrement() {
return U.getAndAddInt(this, VALUE, 1);
}
compare_and_swap()
명령어에 따라 동시에 여러 스레드가 연산을 시도하면, 하나의 스레드만 increment를 성공하고 나머지는 실패할 것이다
이때문에 Atomic 객체에서는 성공할때까지 while 루프를 돈다
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = this.getIntVolatile(o, offset);
} while(!this.weakCompareAndSetInt(o, offset, v, v + delta));
return v;
}
자바에서 volatile
관련 키워드는 CPU 캐쉬가 아닌 메인 메모리에 작업하겠다는 키워드이다
compare_and_swap()
같은 명령어 중에서 결과 값을 int가 아닌 boolean으로 반환하는 명령어를 compare_and_set()
이라고 한다
https://en.wikipedia.org/wiki/Compare-and-swap
https://stackoverflow.com/questions/56216323/how-does-atomicinteger-is-thread-safe