[Java] AtomicInteger와 CAS 연산

MEUN·2024년 11월 26일
0

개요

보통 동시성 이슈의 많은 원인은 값을 조회하고 값을 변경하는 과정이 원자적이지 않기 때문에 발생한다.
자바에서 이러한 연산의 원자성을 지원하기 위해 AtomicInteger와 같은 클래스를 지원한다.



연산의 원자성

해당 연산이 더 이상 나눌 수 없는 단위로 수행되는 경우 이를 원자적 연산이라 한다.
i++과 같은 연산도 i = i + 1의 축약이므로 원자적인 연산은 아니다.

멀티 스레드를 사용하는 경우 원자적 연산은 문제가 되지 않아 대체적으로 문제가 되는 연산은 원자적이지 않은 연산이다.



CAS 연산

락을 사용하지 않고 원자적인 연산을 수행할 수 있게 하며, CAS(Compare-And-Swap, Compare-And-Set) 연산 또는 락 프리 기법이라고도 한다.
소프트웨어가 아닌 CPU 하드웨어에서 지원하는 기능이다.

동작 방식 예시

  1. 현재 변경하고자 하는 값이 예상한 값과 동일한지 비교
  2. 예상한 값과 동일한 경우 바꾸고자 하는 값으로 변경, 동일하지 않은 경우 변경하지 않음

주요 라이브러리

CAS 연산을 사용하는 자바 라이브러리 중 일부이다.

  1. java.util.concurrent 패키지
    • 자바 표준 라이브러리에서 제공하는 라이브러리로, atomic 패키지 내 클래스들은 CAS 연산을 기반으로 동작한다.
    • 이외에 ForkJoinPool도 이 연산을 기반으로 동작한다.
  2. Netty : 비동기 이벤트 기반 네트워크 애플리케이션 프레임워크
  3. Reactor, RxJava : 비동기 스트림 처리에 사용되며 이벤트 기반 라이브러리
  4. JCTools : 경량화된 동시성 도구 모음
  5. Disruptor : 고성능 Non-Blocking 메시징 라이브러리로, CAS 연산 기반의 Ring Buffer를 사용한다.

Spring Webflux를 사용하게 되면 Reactor와 Netty를 접하게 되는데, 해당 라이브러리 모두 CAS 연산을 사용한다는 점을 새롭게 알게 되었다.



AtomicInteger 클래스

멀티 스레드에서 안전한 연산을 지원하기 위한 자바의 클래스로 JDK 1.5부터 지원되었다.
내부적으로 volatile 타입의 int 변수를 가지고 있으며, 해당 변수로 연산을 진행한다.
숫자 기반 클래스를 다루는 도구와 유틸리티에서 동일하게 사용 가능하도록 Number 인터페이스를 확장했다.

public class AtomicInteger extends Number implements java.io.Serializable {
	
    private static final Unsafe U = Unsafe.getUnsafe();
    private static final long VALUE
        = U.objectFieldOffset(AtomicInteger.class, "value");

    private volatile int value;

    /**
     * Creates a new AtomicInteger with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    /**
     * Creates a new AtomicInteger with initial value {@code 0}.
     */
    public AtomicInteger() {
    }
	
    public final int get() {
        return value;
    }
	
    public final void set(int newValue) {
        value = newValue;
    }
	
    public final boolean compareAndSet(int expectedValue, int newValue) {
        return U.compareAndSetInt(this, VALUE, expectedValue, newValue);
    }
	
    public final int incrementAndGet() {
        return U.getAndAddInt(this, VALUE, 1) + 1;
    }
	
	// 중략
}

주요 메소드

get()

public final int get()
  • 현재 값 조회

set()

public final void set(int newValue)
  • 전달 받은 newValue로 값을 세팅

compareAndSet()

public final boolean compareAndSet(int expectedValue, int newValue)
  • 현재 값이 expectedValue와 일치하면 newValue로 변경

incrementAndGet()

public final int incrementAndGet()
  • 현재 값 증가

decrementAndGet()

public final int decrementAndGet()
  • 현재 값 감소

유관 클래스

Intger의 원자적 연산을 지원하는 AtmoicInteger 외에도 각 자료형별로 아래와 같이 클래스가 존재한다.



참고 자료

0개의 댓글