LongAdder — 고성능 long 카운터·합계
LongAdder — 고성능 long 카운터·합계
LongAdder는 여러 변수를 함께 사용해 처음에 0인 long 합을 유지하는 클래스예요. 여러 스레드가 같은 카운터/합계를 동시에 갱신하는 상황에서 단일 AtomicLong보다 훨씬 높은 처리량을 내도록 설계됐어요.
본문
개념 이해하기
add(long) 업데이트가 스레드 간에 경합하면 경합을 줄이기 위해 변수 집합이 동적으로 늘어날 수 있어요. sum()(또는 동등한 longValue())은 합을 유지하는 변수들 전체에 걸친 현재 총합을 반환해요.
이 클래스는 통계 수집처럼 공통 합을 여러 스레드가 갱신하되, 세밀한 동기화 제어가 필요하지 않을 때 AtomicLong보다 선호돼요. 갱신 경합이 낮을 때는 두 클래스가 비슷하지만, 높은 경합에서는 공간 소모가 커지는 대신 처리량이 훨씬 높아요.
public class LongAdder
extends Number
implements Serializable
활용 예 — 빈도 맵
LongAdder는 ConcurrentHashMap과 함께 쓰면 확장 가능한 빈도 맵(히스토그램 또는 멀티셋의 한 형태)을 만들 수 있어요. 예를 들어 ConcurrentHashMap<String,LongAdder> freqs에 카운트를 더하려면, 없으면 초기화하면서 아래처럼 쓸 수 있어요.
freqs.computeIfAbsent(key, k -> new LongAdder()).increment();
이 클래스는 Number를 확장하지만 equals, hashCode, compareTo는 정의하지 않아요 — 인스턴스가 변경될 것으로 예상되므로 컬렉션 키로는 유용하지 않기 때문이에요.
생성자
public LongAdder()
초기 합이 0인 새 adder를 만들어요.
합계 갱신·읽기
public void add(long x)
주어진 값을 더해요.
public void increment() — add(1)과 동등해요.
public void decrement() — add(-1)과 동등해요.
public long sum()
현재 합을 반환해요. 원자적 스냅샷이 아니며, 동시 갱신이 없을 때만 정확해요. 합을 계산하는 동안 발생하는 동시 갱신은 반영되지 않을 수 있어요.
public void reset()
합을 유지하는 변수들을 0으로 재설정해요. 새 adder를 만드는 대신 유용한 대안일 수 있지만, 동시 갱신이 없을 때만 효과가 있어요. 본질적으로 경쟁 조건(racy)이 있으므로, 동시에 갱신하는 스레드가 없다는 걸 알고 있을 때만 사용해야 해요.
public long sumThenReset() — 효과상 sum()을 호출한 뒤 reset()을 호출한 것과 같아요. 멀티스레드 계산 사이의 정지 지점에서 사용할 수 있어요. 이 메서드와 동시에 갱신이 있으면 반환 합이 reset 직전의 최종 값임이 보장되지 않아요.
Number 변환
public long longValue() — sum()과 동등해요. (Number.longValue 구현)
public int intValue() — 좁히기 변환 후 sum()을 int로 반환해요.
public float floatValue() — 넓히기 변환 후 sum()을 float로 반환해요.
public double doubleValue() — 넓히기 변환 후 sum()을 double로 반환해요.
public String toString() — sum()의 문자열 표현을 반환해요. (Object.toString 오버라이드)