ABA Problem이란? 값이 바뀌었다가 원래대로 돌아오면 변경을 놓칠 수 있는 이유 쉽게 이해하기
ABA Problem은 값이 A→B→A로 바뀌었다가 돌아와 CAS가 중간 변경을 놓칠 수 있는 동시성 문제입니다. Version·Hazard Pointer·Lock-Free까지 초보 기준으로 정리합니다.
공유 값이 처음 `A`였는데 Thread 1이 읽는 사이 다른 Thread가 `A→B→A`로 바꿨다면, Thread 1이 돌아와 보면 다시 `A`입니다. CAS는 「예상값과 같으면 교체」만 하므로 `A==A`로 성공해 버릴 수 있습니다. 주차장 10번에 빨간 차 A가 있을 때, 그사이 출차·다른 차 주차·다시 A가 와도 겉모습만 보면 「변화 없음」처럼 보이는 것과 같습니다. 이것이 ABA Problem(ABA 문제)입니다. 이름도 `A→B→A` 순서에서 왔습니다. 단순 Counter처럼 현재 값만 중요하면 문제 없을 수 있지만, Pointer·Lock-Free Stack/Queue·상태 이력이 중요할 때 위험합니다.
Thread1: expected=A 중간에 A→B→A Thread1 CAS: A==A → 성공 (중간 변경 미감지)
Pointer·재사용 · Version · Reclamation
Lock-Free Stack에서 Top Pointer `0x1000`(Node A)을 읽은 뒤 A·B가 Pop되고 같은 주소에 새 Node가 올라오면 Pointer 값은 같아도 객체는 다릅니다. 예전에 읽은 `A.next=B`는 이미 무효인데 CAS가 성공하면 자료구조가 깨질 수 있습니다. 주소가 같다고 같은 Identity가 아닙니다. 해결의 기본은 Value + Version(Tag)을 함께 비교하는 것입니다. `(A,1)`과 `(A,3)`은 값이 같아도 다른 세대입니다. Tagged Pointer·Double-Width CAS는 그 확장입니다. Version Wrap Around도 이론상 가능하므로 Counter 크기를 봅니다. Hazard Pointer·Epoch·RCU는 「아직 보는 Memory를 너무 빨리 재사용하지 않기」로 Pointer ABA를 줄입니다. GC 환경에서도 상태 `OPEN→CLOSED→OPEN`처럼 값만 ABA인 논리는 남을 수 있습니다. Game Entity·Object Pool의 Generation Handle도 같은 사고입니다.
Atomic ≠ 해결 · DB Version · 언제 Lock-Free인가
CAS가 Atomic이어도 ABA는 남을 수 있습니다. Data Race가 없어도 「값 같음 = 변경 없음」 가정이 틀리면 논리 Bug입니다. Race Condition과 관련되지만 단순 Data Race와는 다릅니다. Retry만으로는 CAS가 성공해 버리는 ABA를 못 막습니다. Mutex로 보호하면 피하기 쉽고, 일반 앱은 검증된 Concurrent Library가 안전합니다. Lock-Free는 Latency·진행 보장 요구가 명확할 때만, Memory Ordering·Reclamation·Cache Coherence·Contention 비용을 함께 봅니다. DB Optimistic Locking의 version 컬럼·HTTP ETag If-Match도 「현재 값만 믿지 말고 세대 확인」과 비슷한 설계입니다. Stress Test·Model Checking은 Interleaving Bug 발견에 도움이 되지만 「안 터짐 = 증명」은 아닙니다. SEO 전용 개념은 아닙니다.
예방 요약 Value+Version / Tagged Pointer Hazard Pointer · Epoch · RCU Lock / 검증된 Concurrent 자료구조
자주 묻는 질문
ABA Problem이란?
값이 A→B→A로 바뀌었다가 돌아와 CAS가 현재 값만 비교해 중간 변경을 놓칠 수 있는 동시성 문제입니다.
Atomic CAS여도 생기나?
네. CAS Atomicity와 별개로 「값 같음」만으로는 이력·Identity를 보장하지 않습니다.
Pointer에서만인가?
Pointer에서 유명하지만 상태 값이 다시 같아지는 경우에도 논리적으로 발생할 수 있습니다.
Version이 왜 도움이 되나?
값이 다시 A여도 Version이 다르면 중간 변경이 있었음을 알 수 있습니다.
Hazard Pointer란?
사용 중인 Pointer를 표시해 해당 Memory를 너무 빨리 해제·재사용하지 못하게 하는 기법입니다.
Lock이면 피하나?
자료구조를 Lock으로 적절히 보호하면 변경 중 끼어들기를 막아 ABA를 피하기 쉬워집니다.
정리
ABA Problem = 현재 값이 처음과 같아도 그사이 변경이 없었다고 단정하면 안 되는 문제입니다. `A→B→A` 후 CAS가 `A==A`로 성공해도 History는 「변경 있음」입니다. Value+Version·Memory Reclamation·Lock·검증된 Library로 막습니다. Atomic만으로 모든 동시성 문제가 끝나지 않는 대표 사례입니다. CAS Retry가 심하면 시스템 전체는 진행해도 특정 Thread만 계속 실패하며 기회를 못 얻는 상황이 생길 수 있습니다. 이처럼 경쟁 속에서 특정 작업이 계속 밀리는 현상을 Starvation(기아 상태)이라고 합니다.