AtomicReferenceFieldUpdater — 리플렉션 기반 원자적 필드 업데이터

AtomicReferenceFieldUpdater — 리플렉션 기반 원자적 필드 업데이터

AtomicReferenceFieldUpdater<T,V>는 지정된 클래스의 지정된 volatile 참조 필드를 원자적으로 업데이트할 수 있게 해주는 리플렉션 기반 유틸리티예요. 객체 안의 개별 필드를 통째로 원자적 자료구조로 만들고 싶은데, 매번 클래스 전체를 원자적 wrapper로 감싸기 부담스러울 때 유용해요.

출처: Java API Reference

본문

도입 — 왜 필요한가

같은 노드(node)의 여러 참조 필드가 각각 독립적으로 원자적 업데이트를 받아야 할 때가 있어요. 예를 들어 트리 노드를 아래처럼 선언하면, left/right 같은 필드를 따로따로 원자적으로 바꿀 수 있어요.

class Node {
    private volatile Node left, right;

    private static final AtomicReferenceFieldUpdater<Node, Node> leftUpdater =
        AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "left");
    private static final AtomicReferenceFieldUpdater<Node, Node> rightUpdater =
        AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "right");

    Node getLeft() { return left; }
    boolean compareAndSetLeft(Node expect, Node update) {
        return leftUpdater.compareAndSet(this, expect, update);
    }
    // ... and so on
}

주의할 점은 기존 원자적 클래스들보다 compareAndSet의 보장이 약하다는 거예요. 이 클래스는 필드의 모든 사용이 원자적 접근에 적합한지 보장할 수 없기 때문에, 같은 업데이터에 대한 다른 compareAndSet/set 호출에 한해서만 원자성을 보장해요.

또한 newUpdater에 넘긴 클래스의 인스턴스가 아닌 객체를 T 타입 파라미터로 넘기면 ClassCastException이 던져져요.

생성자

protected AtomicReferenceFieldUpdater()

서브클래스가 사용할 protected do-nothing 생성자예요.

업데이터 생성

public static <U,W> AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass, Class<W> vclass, String fieldName)

주어진 필드를 가진 객체를 위한 업데이터를 만들어 반환해요. 리플렉션 타입과 제네릭 타입이 일치하는지 확인하기 위해 Class 인자가 필요해요.

  • tclass — 필드를 가진 객체의 클래스
  • vclass — 필드의 클래스
  • fieldName — 업데이트할 필드 이름
  • ClassCastException — 필드 타입이 잘못된 경우
  • IllegalArgumentException — 필드가 volatile이 아닌 경우
  • RuntimeException — 클래스가 필드를 갖지 않거나, 타입이 틀렸거나, Java 언어 접근 통제상 호출자에게 필드가 접근 불가능한 경우(중첩된 리플렉션 예외 포함)

원자적 갱신

public abstract boolean compareAndSet(T obj, V expect, V update)

객체의 현재 필드 값이 == 기대값일 때만 update로 원자적으로 설정해요. 성공하면 true를 반환해요. 같은 업데이터의 compareAndSet/set 호출에 대해 원자성이 보장돼요.

  • obj — 필드를 조건부로 설정할 객체
  • expect — 기대값
  • update — 새 값

public abstract boolean weakCompareAndSet(T obj, V expect, V update)

compareAndSet과 유사하지만 **가짜 실패(spurious failure)**가 가능하고 순서 보장이 없어요. compareAndSet의 대안으로는 드물게 적절해요.

public abstract void set(T obj, V newValue)

객체의 필드를 주어진 newValue로 설정해요. 이후의 compareAndSet 호출에 대해 volatile store처럼 동작이 보장돼요.

public abstract void lazySet(T obj, V newValue) — 필드를 주어진 값으로 "결국(Eventually)" 설정해요. (JDK 1.6+)

public abstract V get(T obj) — 객체의 필드 현재 값을 반환해요.

public V getAndSet(T obj, V newValue) — 필드를 주어진 값으로 원자적으로 설정하고 이전 값을 반환해요.

함수 적용 계열

public final V getAndUpdate(T obj, UnaryOperator<V> updateFunction) — 함수 적용 결과로 필드를 원자적으로 갱신하고 이전 값을 반환해요. 함수는 스레드 경합 시 재적용될 수 있어 부작용이 없어야 해요. (JDK 1.8+)

public final V updateAndGet(T obj, UnaryOperator<V> updateFunction) — 같은 방식으로 갱신하되 갱신된 값을 반환해요. (JDK 1.8+)

public final V getAndAccumulate(T obj, V x, BinaryOperator<V> accumulatorFunction) — 현재 값과 x에 함수를 적용한 결과로 갱신하고 이전 값을 반환해요. 함수는 현재 값이 첫 번째 인자, 주어진 값이 두 번째 인자로 적용돼요. 부작용 없는 2-인자 함수여야 해요. (JDK 1.8+)

public final V accumulateAndGet(T obj, V x, BinaryOperator<V> accumulatorFunction) — 같은 방식으로 갱신하되 갱신된 값을 반환해요. (JDK 1.8+)

더 알아보기 (Learn more)