|
| 1 | +# CAS - 동기화와 원자적 연산 |
| 2 | + |
| 3 | +## 원자적 연산 |
| 4 | +컴퓨터 과학에서 사용하는 **원자적 연산**(atomic operation)의 의미는 해당 연산이 더 이상 나눌 수 없는 단위로 수행된 다는 것을 의미 |
| 5 | + |
| 6 | +> 예시와 함께 알아보자 |
| 7 | +
|
| 8 | +| 구분 | 구현 | 성능 | 원자성 보장 | 추가 설명 | |
| 9 | +|:---:|:-------------------------------------------:|:------------------:|:-------:|:-----------------------------------------------------------------:| |
| 10 | +| BasicInteger | int 형 타입 value<br>`synchronized` 미사용 | 가장 빠름 | X | 단일 스레드에서만 사용 가능 | |
| 11 | +| VolatileInteger | `volatile` int 형 타입 value<br>`synchronized` 미사용 | 느림 | X | 연산을 위한 안전한 임계 영역이 존재하지 않음 | |
| 12 | +| SyncInteger | int 형 타입 value<br>`synchronized` 사용 | 가장 느림 | O | 안전한 임계 영역 존재 | |
| 13 | +| MyAtomicInteger | `AtomicInteger` 사용 | synchronized 보다 빠름 | O | `synchronized` , `Lock(ReentrantLock)` 을 사용하는 경우보다 1.5 ~ 2배 정도 빠름 | |
| 14 | + |
| 15 | +락 기반(`synchronized`, `Lock`)의 경우 안전한 임계 영역이 존재하지 하지만, 값을 조회하고, 수정할 때 락 취득을 위한 코스트가 발생 |
| 16 | + |
| 17 | +락 프리(`Atomic`) 기법은 CAS(Compare-And-Swap, Compare-And-Set) 연산 기반으로 락을 사용하지 않고 원자적 연산을 지원 |
| 18 | +- CAS 연산은 락을 완전히 대체하는 것은 아니고, CPU 하드웨어 기반으로 **작은 단위의 일부 영역에 적용** |
| 19 | + |
| 20 | +<br> |
| 21 | + |
| 22 | +## Atomic 클래스 |
| 23 | +### 동작 |
| 24 | + |
| 25 | +```mermaid |
| 26 | +--- |
| 27 | +title: CAS 연산 |
| 28 | +--- |
| 29 | +flowchart |
| 30 | + ai[AtomicInteger] --> atomic{원자적 연산인가?} |
| 31 | + atomic -->|Yes| getAndSet["조회(get), 대입(set)"] |
| 32 | + getAndSet --> volatile[volatile value] |
| 33 | +
|
| 34 | + atomic -->|No| compare["비교 연산(incr, compareAndSet)"] |
| 35 | + compare --> compareAndOperate{"비교 연산 실행<bR>(volatile 조회 후 연산)"} |
| 36 | + compareAndOperate -->|"return true<br>(기댓값과 실제값이 같은 경우)"| success[저장] |
| 37 | + success --> volatile |
| 38 | + compareAndOperate -->|"return false<br>(기댓값과 실제값이 다른 경우)"| fail[처음부터 재시도] |
| 39 | + fail --> compare |
| 40 | +``` |
| 41 | + |
| 42 | +> CAS를 통해 스핀락(Spin Lock)을 구현하는 경우 `compareAndSet` 메서드 활용 |
| 43 | +> |
| 44 | +> Spin Lock은 락을 획득할 때까지 계속 반복문을 돌면서 대기하기 때문에 락 취득을 위해 대기하는 시간이 길어지는 경우(e.g. CPU 연산이 오래 걸리는 경우) 성능 저하가 발생할 수 있음 (CPU 자원 계속해서 소모) |
| 45 | +
|
| 46 | +스레드가 락을 획득하기 위해 대기하지 않기 때문에 대기 시간과 오버헤드가 줄어들기 때문에 성능 향상 |
| 47 | + |
| 48 | +충돌(연산 시 기댓값과 실제값이 다른 경우)이 빈번하게 발생하는 환경에서는 성능에 문제가 될 수 있음 |
| 49 | +- 여러 스레드가 자주 동시에 동일한 변수의 값을 변경하려고 시도할 때, CAS는 자주 실패하고 재시도해야 하므로 성능 저하가 발생할 수 있음 |
| 50 | +- 이런 상황에서는 반복문을 계속 돌기 때문에 CPU 자원을 많이 소모하게 됨 |
| 51 | + |
| 52 | +간단한 CPU 연산에는 락(Lock)보다는 CAS를 사용하는 것이 효과적 |
| 53 | + |
| 54 | +<br> |
| 55 | + |
| 56 | +### 비교 |
| 57 | + |
| 58 | +| 구분 | 동기화 락(Lock) 방식 | CAS(Compare-And-Swap) 방식 | |
| 59 | +|:---:|:-----------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------| |
| 60 | +| 접근 방법 | 비관적(pessimistic) 접근법<br>(다른 스레드가 방해할 것이다) | 낙관적(optimistic) 접근법<br>(대부분의 경우 충돌이 없을 것이다) | |
| 61 | +| 설명 | 데이터에 접근하기 전에 항상 락을 획득<br>다른 스레드의 접근을 막음 | 락을 사용하지 않고 데이터에 바로 접근<br>충돌이 발생하면 그 때 재시도 | |
| 62 | +| 장점 | 충돌 관리: 하나의 스레드만 리소스에 접근할 수 있으므로 충돌 발생 X<br><br>안정성: 복잡한 상황에서도 일관성 있는 동작 보장<br><br>CPU 절약: 락을 대기하는 스레드는 CPU를 거의 사용하지 않음 | 낙관적 동기화: 락을 걸지 않고도 값을 안전하게 업데이트<br><br>충돌이 적은 경우 성능 향상: 락 프리(Lock-Free) 기반으로 락을 사용하지 않기 때문에 락을 획득하기 위해 대기하는 시간 X | |
| 63 | +| 단점 | 락 획득 대기 시간 증가: 스레드가 락을 획득하기 위해 대기해야 하므로 대기 시간이 길어질 수 있음<br><br>컨텍스트 스위칭 오버헤드: 락을 사용하면 락 획득을 대기하는 시점과 또 락을 획득하는 시점에 스레드의 상태가 변경되어 컨텍스트 스위칭이 발생할 수 있음 | 충돌이 빈번한 경우 성능 저하: 여러 스레드가 동시에 동일한 변수에 접근하여 업데이트를 시도할 때 충돌이 발생할 수 있음<br><br>스핀 락과 유사한 오버헤드: 충돌 시 반복적인 재시도를 하므로, 이 과정이 계속 반복되면 스핀 락과 유사한 성능 저하 발생 | |
| 64 | + |
| 65 | +<br> |
| 66 | + |
| 67 | +### 요약 |
| 68 | +일반적으로 동기화 락을 사용하고, 아주 특별한 경우에 한정해서 CAS를 사용해서 최적화해야 함 |
| 69 | +- 빨리 끝나거나 단순한 연산(e.g. 카운트)에서는 CAS를 사용하는 것이 효과적 |
| 70 | +- 오래 걸리거나 복잡한 연산(e.g. DB I/O 작업, Network I/O 작업)에서는 동기화 락 사용 |
| 71 | + |
| 72 | +우리가 일반적으로 사용하는 많은 자바 동시성 라이브러리 및 동기화 컬렉션들은 성능 최적화를 위해 CAS 연산을 적극 활용하고 있음 |
| 73 | +- 따라서 실무에서 직접 CAS 연산을 사용하는 사용하는 일은 매우 드뭄 |
| 74 | +- 대신에 CAS 연산을 사용해서 최적화 되어 있는 라이브러리들을 이해하고 편리하게 사용할 줄 알면 충분함 |
0 commit comments