관리 메뉴

백엔드 엔지니어 이재혁

[JAVA] CAS 락 구현 본문

Java

[JAVA] CAS 락 구현

alex00728 2025. 6. 10. 12:26

CAS 연산을 활용해 동기화 락 (synchronized, Lock 인터페이스) 없이 락을 구현해보자.

 

우선 잘못 만든 예제를 확인해보자.

public class SpinLockBad {
    private volatile boolean lock = false;
    
    public void lock() {
        log("락 획득 시도");
        while (true) {
            if (!lock) { // 1. 락 사용 여부 확인
                // sleep(100); // 문제 상황 확인용, 스레드 대기
                lock = true; // 2. 락의 값 변경
                break; // while 탈출
            } else {
                // 락을 획득할 때까지 스핀 대기(바쁜 대기) 한다.
                log("락 획득 실패 - 스핀 대기");
            }
        }
        log("락 획득 완료");
    }

    public void unlock() {
        lock = false; // 락 반납
    }
}

 

 

위 구현은 어떤 문제가 있을까?

  1. 락 사용 여부 확인
  2. 락의 값 변경

이 두 과정 사이에 다른 스레드가 개입할 수 있는 여지가 있으면 안된다!

 

이럴 때 CAS 연산을 떠올릴 수 있다면 좋은 해결책임을 알 수 있다.

락의 현재 상태를 확인하는 것과 그 값을 변경하는 것을 동시에 할 수 있다.

 

SpinLock

lock() 메서드를 다음과 같이 개선할 수 있다.

public void lock() {
    log("락 획득 시도");
    while (!lock.compareAndSet(false, true)) {
        // 락을 획득할 때까지 스핀 대기(바쁜 대기) 한다.
        log("락 획득 실패 - 스핀 대기");
    }
    log ("락 획득 완료");
}

 

CAS 연산을 활용해 다음 두 연산을 하나의 원자적 연산으로 바꿨다!

  1. 락 사용 여부 확인: lock의 값이 false면
  2. 락의 값 변경: lock의 값을 true로 변경해라

위 방식으로 작동하는 lock이 SpinLock이다. 락 획득을 실패해도 while문을 계속 돌아가는 것을 보고 Spin이라고 한다. 락 획득에 실패해도 CPU 자원을 계속 소모하기 때문에 락 획득 대기 시간이 길어지면 안된다. (충돌 가능성이 낮아야 한다.)

 

CAS를 활용할 수 있는 순간을 떠올리기

Javascript로 프로그래밍을 할 때 다음과 같은 방식으로 코드를 짰던 것이 생각났다.

if (num++) {
	// run
}

 

Javascript에서는 상태 확인과 그 값을 새로 대입하는 과정을 if 조건 안에 한 번에 넣을 수 있다. 이런 경우에 CAS 연산을 생각해볼 수 있을 것 같다. (그렇다고 위 조건문이 원자적 연산으로 돌아간다는게 절대 아니다. 만약 Javascript가 멀티스레딩 환경으로 돌아간다면 문제가 생길 수 있다.)

"상태 확인과 값 변경"을 원자적으로 처리해볼 수 있는 순간을 떠올리기 위한 아이디어로 기억해두자.

 

CAS vs 동기화 락 장단점 정리

  CAS 동기화 락
장점 락 프리(Lock-Free)
병렬 처리가 더 효율적이게 될 수 있다
충돌 관리, 안정성: 복잡한 상황에서도 일관성 있는 동작
스레드 대기: 락을 대기하는 스레드는 CPU를 거의 사용하지 않음
단점 충돌이 빈번한 경우
충돌 시 반복적인 재시도
자주 반복되면 성능 저하
락 획득 대기: 락 획득을 위한 대기 시간이 길어질 수 있다
컨텍스트 스위칭 오버헤드: 스레드 상태 변경으로 인한 오버헤드

 

 

'Java' 카테고리의 다른 글

[Java] Executor 프레임워크 기본  (0) 2025.06.25
[Java] 컬렉션 프레임워크와 동시성  (0) 2025.06.13
[Java] CAS 연산 활용  (1) 2025.06.09
[Java] Atomic과 CAS 연산  (0) 2025.06.09
[Spring] Spring의 주입 방식  (0) 2025.06.09