CAS 연산을 활용한 SpinLock을 알아보자

개발자 이상규·2024년 11월 23일

CS

목록 보기
3/3
post-thumbnail

CAS 연산이란

락을 걸지 않고 원자적인 연산을 수행할 수 있는 방법이 있는데 이것을 CAS(Campare-And-Swap) 연산이라 합니다.
이 방법은 락을 사용하지 않기 때문에 Lock-Free 기법이라합니다.
CAS 연산은 락을 완전히 대체하는 것은 아니고 작은 단위의 일부 영역에 적용할 수 있습니다.
기본은 락을 사용하고 특별한 경우에 CAS를 적용할 수 있다고 생각하면 됩니다.

안전한 임계 영역이 필요하지만 연산이 길지 않고 아주 짧게 끝날때 사용해야한다.
단순히 숫자 값의 증가, 자료 구조의 데이터 추가와 같이 CPU 싸이클이 금방끝나는 연산에 사용하면 효과적이다.
반면에 데이터베이스를 기다린다거나, 다른 서버의 요청을 기다리는 것 처럼 수 밀리초 이상의 시간이 걸리는 작업이라 면 CAS를 사용하는 것 보다 동기화 락을 사용하거나 스레드가 대기하는 방식이 더 효과적이다.

SpinLock이란

SpinLock 동작 방식

  1. 잠금 상태 확인: 스레드는 공유 자원에 접근하기 전에 잠금(Lock)을 확인합니다.
  2. 잠금 획득 대기: 잠금이 이미 다른 스레드에 의해 사용 중인 경우, Spin Lock은 CPU를 점유한 상태에서 루프를 돌며 잠금이 해제되기를 기다립니다. 이 과정을 "스핀(spin)"이라고 합니다.
  3. 잠금 해제 후 사용: 잠금이 해제되면, 현재 스레드가 잠금을 획득하고 자원을 사용합니다.

예제 코드

public class SpinLock {

    private volatile AtomicBoolean lock = new AtomicBoolean(FALSE);

    public void lock() {
        log("Lock 획득 시도");
        while (!lock.compareAndSet(FALSE, TRUE)) {
            log("락 획득 실패 - 스핀 대기");
        }
        log("락 획득 완료");
    }

    public void unlock() {
        lock.set(FALSE);
        log("락 반납 완료");
    }
}

Lock vs CAS

CAS의 장점

  1. 낙관적 동기화: 락을 걸지 않고도 값을 안전하게 업데이트 할 수 있습니다. CAS는 충동이 자주 발생하지 않을 것이라고 가정하고 충돌이 적은 환경에서 높은 성능을 발휘합니다.
  2. Lock-Free: CAS는 락을 사용하지 않기 떄문에, 락을 획득하기 위해 대기하는 시간이 없습니다. 따라서 쓰레드가 블로킹되지 않으며 병렬처리에 더 효율적일 수 있습니다.

CAS의 단점

  1. 충돌이 빈번한 경우: 여러 스레드가 동시에 동일한 변수에 접근하여 업데이트를 시도할 때 충돌이 발생할 수 있습니다.
    충돌이 발생하면 CAS는 루프를 돌며 재시도해야 하며, 이에 따라 CPU 자원을 계속 소모할 수 있다. 반복적인 재시도로 인해 오버헤드가 발생할 수 있습니다.
  2. 스핀락과 유사한 오버헤드: CAS는 충돌 시 반복적인 재시도를 하므로, 이 과정이 계속 반복되면 스핀락과 유사한
    성능 저하가 발생할 수 있다. 특히 충돌 빈도가 높을수록 이런 현상이 나타납니다.

동기화 락의 장점

  1. 충돌 관리: 락을 사용하면 하나의 스레드만 리소스에 접근할 수 있으므로 충돌이 발생하지 않는다. 여러 스레드가
    경쟁할 경우에도 안정적으로 동작합니다.
  2. 안정성: 복잡한 상황에서도 락은 일관성 있는 동작을 보장합니다.
  3. 스레드 대기: 락을 대기하는 스레드는 CPU를 거의 사용하지 않습니다.

동기화 락의 단점

  1. 락 획득 대기 시간: 스레드가 락을 획득하기 위해 대기해야 하므로, 대기 시간이 길어질 수 있습니다.
  2. 컨텍스트 스위칭 오버헤드: 락을 사용하면, 락 획득을 대기하는 시점과 또 락을 획득하는 시점에 스레드의 상태가
    변경된다. 이때 컨텍스트 스위칭이 발생할 수 있으며, 이로 인해 오버헤드가 증가할 수 있습니다.



CAS를 활용한 원자적인 연산은 스레드가 쪼갤 수 없어 여러 스레드가 동시에 실행해도 안전하다.
원자적으로 만든 락 덕분에 무거운 동기화 작업 없이 가벼운 락을 만들 수 있다.

동기화 락을 사용하는 경우 스레드가 락을 획득하지 못하면 BLOCKED, WAITING 등 상태가 변한다.
또한 대기상태의 스레드를 깨워야 하는 무겁고 복잡한 과정이 추가로 들어간다.
CAS를 활용한 락 방식은 사실 락이 없다. 단순히 while문을 반복할 뿐이다. 따라서 대기하는 스레드도 RUNNABLE 상태를 유지하면서 가볍고 빠르게 동작한다.

하지만 while문을 반복하면 락을 기다리는 스레드가 CPU를 계속 사용하면서 대기하게 된다.





Reference:

  • 김영한의 실전 자바 - 고급 1편, 멀티스레드와 동시성
profile
Contact: leeeesanggyu@gmail.com

0개의 댓글