본문 바로가기
language/java

CAS(Compare And Swap) 완벽 이해하기

by 죄니안죄니 2026. 5. 29.
반응형

CAS(Compare And Swap) 완벽 이해하기

이전 글에서 배운 내용을 먼저 정리해보자.

멀티스레드
 ↓
Race Condition 발생
 ↓
synchronized 사용
 ↓
문제 해결
 

그런데 문제가 하나 있다.

synchronized는 안전하지만 비싸다.
 

왜냐하면:

Lock 획득
 ↓
대기
 ↓
Context Switch
 ↓
Lock 해제
 

과정이 필요하기 때문이다.

그래서 CPU 제조사와 JVM 개발자들은 고민했다.

락을 사용하지 않고도 안전하게 값을 변경할 수 없을까?

여기서 등장한 것이:

CAS (Compare And Swap)

이다.


1. CAS란?

한 줄 정의.

현재 값이 내가 예상한 값이면 변경하고,
아니면 실패하는 원자적(Atomic) 연산
 

2. 이름 의미

CAS

Compare
↓
비교하고

And

Swap
↓
교체
 

3. 예제

현재 값

count = 10
 

Thread A

10이라고 생각
 

CAS 요청

CAS(10, 11)
 

의미

현재 값이 10이면

11로 바꿔라
 

4. 결과

현재 값

10
 

이므로

11
 

변경 성공.


5. 다른 경우

현재 값

count = 12
 

CAS

CAS(10, 11)
 

결과

실패
 

왜?

내가 예상한 값
=
10

실제 값
=
12
 

다르기 때문.


6. 중요한 특징

CAS는

비교 + 변경
 

한 번에
 

수행된다.


7. 왜 중요할까?

만약

비교

↓

변경
 

이 따로 수행되면

중간에 다른 Thread가 끼어들 수 있다.


CAS는 CPU가 제공하는 특수 명령어로

비교
+
변경
 

을 하나의 원자적 연산으로 처리한다.


8. Atomic이란?

Atomic

더 이상 쪼갤 수 없는 연산
 

예:

 
count++;
 

사실은

읽기

+

1

+

쓰기
 

3단계.


Atomic 아님.


9. CAS는 Atomic

비교
+
쓰기
 

한 번에 수행됨.


10. synchronized와 비교

synchronized

문 잠금
 ↓
작업
 ↓
문 열기
 

CAS

문 안 잠금
 ↓
변경 시도
 ↓
실패하면 재시도
 

11. CAS 동작 예시

초기값

count = 0
 

Thread A

0 읽음
 

Thread B

0 읽음
 

Thread A

CAS(0,1)
 

성공.


현재 값

1
 

Thread B

CAS(0,1)
 

실패.


왜?

현재 값
=
1
 

12. Thread B는?

다시 시도.

1 읽음

↓

CAS(1,2)

↓

성공
 

13. 이것이 Lock-Free

중요.

락 없이 동작
 

한다.


14. Lock-Free란?

락을 사용하지 않고

동시성 문제를 해결하는 기법.


15. JVM에서 CAS

실제로는

 
Unsafe.compareAndSwapInt()
 

같은 JVM 내부 명령 사용.

(최신 JDK는 VarHandle 기반)


16. CPU 명령어

대표적으로

CMPXCHG
 

명령 사용.

(x86 기준)


CPU가 직접 지원.


17. Java에서 어디에 쓰일까?

대표적으로

 
AtomicInteger
 

18. 예제

 
AtomicInteger count =
        new AtomicInteger();
 

증가

 
count.incrementAndGet();
 

19. 내부 구현

대략

 
for (;;) {

    int current = value;

    int next = current + 1;

    if (CAS(current, next)) {
        return next;
    }
}
 

20. 왜 for 무한루프?

CAS 실패 가능.


실패하면

다시 읽기
 ↓
다시 CAS
 

반복.


21. Spin Lock

이 방식을

Spin
 

이라고 한다.


계속 돌면서 시도.


22. 장점

락 없음.


Context Switch 없음.


빠름.


23. 단점

경쟁 심하면

실패
 ↓
재시도
 ↓
실패
 ↓
재시도
 

계속 반복.


CPU 사용량 증가.


24. 언제 유리할까?

경쟁 적을 때.


예:

99% 성공
 

매우 빠름.


25. 언제 불리할까?

경쟁 심할 때.


100개 Thread
 

가 동시에 수정.


CAS 실패 폭증.


26. 그래서

CAS ≠ 무조건 좋음
 

상황에 따라 다름.


27. ABA 문제

CAS의 유명한 문제.

면접 단골.


현재 값

A
 

Thread A

A 읽음
 

그 사이

Thread B

A → B → A
 

변경.


28. Thread A 관점

A 그대로네?
 

CAS 성공.


하지만 실제로는

중간에 변경됨
 

이것이

ABA Problem
 

29. 해결

대표적으로

 
AtomicStampedReference
 

사용.


30. 버전 관리

예:

A, version=1

↓

B, version=2

↓

A, version=3
 

이제 구분 가능.


31. synchronized vs CAS


항목 synchronized CAS
Lock 사용 O X
Context Switch O X
Atomicity O O
경쟁 적을 때 느림 빠름
경쟁 심할 때 안정적 재시도 증가

32. CAS와 volatile 관계

매우 중요.


AtomicInteger 내부

 
private volatile int value;
 

왜?

CAS는

Atomicity
 

제공.


volatile은

Visibility
 

제공.


둘 다 필요.


33. 실제 AtomicInteger 구조

개념적으로

 
class AtomicInteger {

    volatile int value;

    incrementAndGet() {
        CAS ...
    }
}
 

34. 실무에서 자주 쓰는 Atomic 클래스

대표:

AtomicInteger

 
AtomicInteger
AtomicLong
AtomicBoolean
AtomicReference
 

35. 면접 단골 질문

Q. CAS란?

현재 값이 예상값과 같을 때만 변경하는 원자적 연산
 

Q. CAS는 Lock을 사용하는가?

아니오
 

Q. CAS 실패 시?

재시도
 

Q. AtomicInteger는 무엇으로 구현되나?

volatile + CAS
 

Q. CAS 문제점은?

ABA 문제
 

36. 핵심 흐름 요약

값 읽기
 ↓
예상값 계산
 ↓
CAS 시도
 ↓
성공 → 종료

실패 → 재시도
 

37. synchronized → volatile → CAS 흐름

지금까지 배운 내용을 연결하면:

Race Condition
 ↓
synchronized
 ↓
Lock 비용 발생

↓

volatile
 ↓
Visibility 해결

↓

CAS
 ↓
Lock-Free Atomic 연산
 

38. 가장 중요한 핵심 한 줄

CAS는 "현재 값이 예상한 값과 같을 때만 변경"하는 CPU 수준의 원자적 연산이며, AtomicInteger 같은 Lock-Free 동시성 클래스의 핵심 기반 기술이다.
 

39. 다음 글 예고

다음 글은:

Atomic 클래스 (AtomicInteger, AtomicLong 등)

입니다.

CAS를 실제 Java 코드에서 어떻게 활용하는지, 그리고 왜 count++ 대신 AtomicInteger.incrementAndGet()을 사용하는지 실무 관점에서 깊게 설명하게 됩니다.

반응형

댓글