AtomicInteger | int 값을 원자적으로 다룸 |
AtomicLong | long 값을 원자적으로 다룸 |
AtomicBoolean | boolean 값을 원자적으로 다룸 |
AtomicReference<T> | 객체 참조를 CAS로 변경 |
AtomicStampedReference<T> | 참조 + 버전(stamp) 을 같이 CAS |
AtomicMarkableReference<T> | 참조 + boolean 마크를 같이 CAS |
AtomicIntegerArray | int[] 배열의 원소를 CAS로 다룸 |
AtomicLongArray | long[] 배열의 원소를 CAS로 다룸 |
AtomicReferenceArray<T> | T[] 배열의 원소를 CAS로 다룸 |
AtomicIntegerFieldUpdater<T> | 객체 내 int 필드 업데이트용 |
AtomicLongFieldUpdater<T> | 객체 내 long 필드 업데이트용 |
AtomicReferenceFieldUpdater<T, V> | 객체 내 참조형 필드 업데이트용 |
위의 클래스들을 어떻게 활용할 수 있을 지 생각해보자.
1. CAS가 왜 더 효율적인가?(사용하기 앞서)
1-1. Atomic 클래스는 CAS 특성 상 경합이 적은 로직에 사용해야 한다.
=> CAS는 실패하면(낙관적 락) While 문으로 SPIN을 돈다.
=> 경합이 많을 수록 계속된 While문이 돌수 있음
=> CPU 낭비 가능
1-2. 경합이 적으면 굳이 CAS를 쓸 필요 있나?
=> 경합이 적다고 트래픽이 낮은 것이 아님
=> 무한대에 가까운 사용자가 각각 무한대의 데이터 중 하나의 데이터에 접근한다면, 이는 경합이 적지만 고 트래픽 상황
=> 이때, 락을 사용한다면, 짧은 CPU 연산이 필요한 작업도 블로킹 동작 발생
1-3. 블로킹 동작보다 스핀이 빠른가?
=> 블로킹 동작을 한다는 것은
=> 커널 모드로 진입한다.(모드 전환 오버헤드)
=> 락을 획득하지 못한 블록한 쓰레드를 대기 큐에 등록한다.(대기 큐 삽입 비용)
=> 컨텍스트 스위칭을 한다.(문맥 교환 비용)
=> 위의 3가지 비용이 든다.
=> 즉, 2번의 SPIN만 돌면 될 것을, 위의 3가지 비용을 지불한다는 뜻이다.(당연히, 적은 스핀이 블로킹 비용보다 효율적이다.)
1-4. 때문에, 짧은 CPU 연산이 있는 경합이 적은 서비스는 CAS를 도입함으로 효율을 챙길 수 있는 것이다.
**블로킹 동작 VS 스핀(스핀이 ns일 경우)**
위의 표에서 스핀은 아주 간단한 CPU 연산을 하고, 경합이 매우 매우 적다고 가정한다.
'성능 최적화' 카테고리의 다른 글
락과 동시성 문제 (0) | 2025.04.23 |
---|---|
비관 락, 컨디션 변수, 세마포어, 블록 (0) | 2025.04.21 |
트랜잭션 격리 수준과 락 (0) | 2025.04.20 |
성능 최적화 (0) | 2025.04.19 |
CAS, 낙관적 락, Atomic VS LongAdder(Accumulator) (0) | 2025.04.17 |