Java의 멀티스레드와 동시성문제 간단 정리[4.CAS와 원자적연산]

0
post-thumbnail

원자적 연산

원자적 연산은 더 이상 나눌 수 없는 단위로 수행되는 연산을 의미. 중단되지 않고 다른 연산과 간섭 없이 완전히 실행되거나 전혀 실행되지 않는 성질을 가지고 있다.

예를 들어, 다음 연산은 원자적 연산이다.

int i = 10;
i = 1; // 원자적 연산

하지만 다음 연산은 원자적 연산이 아니다.

i = i + 1; // 원자적 연산이 아님

원자적 연산이 아닌 이유는 다음의 단계로 연산이 실행되기 때문이다.

  1. 오른쪽에있는 i 의값을읽는다. i 의 값을 10이라고 가정 해 본다.
  2. 읽은 10에 1을 더해서 11을 만든다.
  3. 더한 11을 왼쪽의 i 변수에 대입한다.

CAS(Compare-And-Swap)

CAS(Compare-And-Swap)는 락을 사용하지 않고도 원자적 연산을 수행할 수 있는 기법이다. 이는 동기화의 비용을 줄이고 성능을 향상시키기 위해 사용된다. 자바에서는 AtomicInteger, AtomicBoolean과 같이 Atomic이라는 이름이 붙은 클래스를 통해 CAS를 지원하며, 동시성과 관련되어 구현된 코드에서 최적화를 위한 기법으로 CAS를 사용하는 경우가 있다.

CAS 연산

CAS 연산은 현재 값과 기대 값을 비교하여 일치하면 새로운 값으로 교체하는 연산. 예를 들어, AtomicInteger를 사용한 CAS 연산은 다음과 같다

AtomicInteger atomicInteger = new AtomicInteger(0);
boolean result = atomicInteger.compareAndSet(0, 1); // 현재 값이 0이면 1로 변경

이와 같은 CAS연산을 통하면, CPU에서 원자적연산으로 처리하는 결과를 갖는다. (즉 원자적 연산이 된다)

CAS의 장점

  1. 낙관적 동기화: 락을 걸지 않고도 값을 안전하게 업데이트할 수 있다.
  2. 락 프리(Lock-Free): 락을 사용하지 않기 때문에, 락을 획득하기 위해 대기하는 시간이 없다. 따라서 스레드가 블로킹되지 않으며, 병렬 처리에 더 효율적으로 동작한다.

CAS의 단점

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

실무에서의 활용

CAS는 충돌 가능성이 낮은 환경에서 매우 효율적이다. 예를 들어, 주문 수를 증가시키는 단순한 연산에서 CAS를 사용하면, 충돌이 발생할 가능성이 매우 낮기 때문에 높은 성능을 발휘할 수 있다. 그러나 데이터베이스 결과를 기다리거나 다른 서버의 요청을 기다리는 것처럼 오래 기다리는 작업에서는 락을 사용하는 것이 더 적절하다.
이 CAS 연산에 대한 별도의 예시 코드는 정리 하지 않는다. 왜냐하면 직접 구현해서 사용하는 경우는 없기 때문이다. Atomic{xxx}와 같이 CAS 연산을 사용하는 라이브러리의 내부구현에 대한 기초적인 개념적 이해와 이런 CAS 연산을 사용하는 라이브러리들을 잘 사용하는 정도면 충분하기 때문이다.

profile
오늘도 머릿속에 인덱스를 새겨넣는 개발자

0개의 댓글