이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • volatile Atomic Concurrent Collection에서 붙잡아야 할 핵심 판단 기준은 무엇인가?
  • 가시성, 원자성, 자료구조 안전성을 구분한다라는 기준은 어떤 JVM/OS 신호로 확인되는가?
  • Java/Spring 코드에서 Feature flag나 shutdown flag처럼 최신 값 관찰이 핵심이면 volatile이 맞을 수 있다 흐름은 어떤 장애 신호로 드러나는가?

개요

이 문서의 주제는 volatile Atomic Concurrent Collection이다. volatile, Atomic, concurrent collection의 목적과 한계를 정리한다.

먼저 붙잡을 기준은 다음 문장이다. 가시성, 원자성, 자료구조 안전성을 구분한다. volatile, Atomic*, concurrent collection은 모두 동시성 도구지만 보장 범위가 다르며, 하나를 다른 하나의 빠른 대체재로 쓰면 데이터 오류가 남는다.

핵심 모델

  • 판단 기준: 가시성, 원자성, 자료구조 안전성을 구분한다.
  • volatile은 최신 값 가시성에는 도움이 되지만 복합 연산 원자성은 보장하지 않는다.
  • Atomic 계열은 단일 변수의 compare-and-set 기반 원자 갱신에 적합하다.
  • Concurrent collection은 자료구조 내부 동시 접근을 보호하지만 value 객체의 thread-safe까지 보장하지 않는다.
  • Get-then-put, check-then-act는 concurrent collection을 써도 race가 남을 수 있다.
  • 필요한 보장이 가시성, 단일 변수 원자성, 자료구조 안전성, 복합 불변식 중 무엇인지 먼저 정해야 한다.

문서별 핵심 구분

  • volatile은 가시성 중심이고 복합 연산 원자성을 보장하지 않는다.
  • Atomic은 단일 변수의 원자 연산에 강하다.
  • Concurrent collection도 내부 value 객체의 thread-safe를 보장하지 않는다.

원리

volatile은 한 thread가 쓴 값을 다른 thread가 보게 하는 가시성에 강하다. 하지만 count++처럼 읽고 계산하고 쓰는 복합 연산을 원자적으로 만들지는 않는다.

AtomicInteger, AtomicLong, AtomicReference는 단일 변수의 원자적 갱신에 적합하다. 여러 필드를 함께 바꾸거나 invariant를 유지해야 하면 Atomic 하나로는 부족할 수 있다.

ConcurrentHashMap 같은 collection은 자료구조 내부 동시 접근을 안전하게 하지만, value 객체 자체의 mutable 상태까지 안전하게 만들어 주지는 않는다.

따라서 도구 선택은 “lock보다 빠른가”가 아니라 필요한 보장이 가시성인지, 단일 변수 원자성인지, 자료구조 동시 접근인지, 복합 불변식 보호인지로 결정한다.

Java/Spring 연결

  • Feature flag나 shutdown flag처럼 최신 값 관찰이 핵심이면 volatile이 맞을 수 있다.
  • 요청 카운터나 성공/실패 횟수처럼 단일 숫자 갱신은 AtomicLong이나 LongAdder 후보가 된다.
  • Spring singleton cache에 ConcurrentHashMap을 써도 value 내부 객체가 mutable이면 race가 남는다.
  • computeIfAbsent, merge, compute 같은 atomic map operation을 쓰지 않으면 get-then-put race가 생길 수 있다.
  • 분산 인스턴스의 counter나 cache 정합성은 JVM atomic 도구가 아니라 외부 저장소 전략이 필요하다.
  • Spring 코드 리뷰에서는 volatile Atomic Concurrent Collection 판단 기준이 어떤 thread, pool, timeout, resource 설정으로 드러나는지 확인한다.

코드와 설정 예시

private final AtomicLong successCount = new AtomicLong();
private final ConcurrentHashMap<String, SessionState> sessions = new ConcurrentHashMap<>();
 
public void recordSuccess() {
    successCount.incrementAndGet();
}

AtomicLong은 단일 counter 증가에는 적합하다. 하지만 sessions의 내부 map이 안전하다는 사실이 SessionState 객체의 모든 field 변경까지 안전하다는 뜻은 아니다.

관찰 명령

jcmd <pid> Thread.print
top -H -p <pid>
curl -s localhost:8080/actuator/metrics/jvm.threads.live

volatile Atomic Concurrent Collection 관찰 명령은 결론이 아니라 가설 확인 도구다. 운영 중에는 반복 간격, 대상 PID, 수집 시각을 함께 남기고 출력이 큰 명령은 범위를 제한한다.

지표 해석

신호먼저 의심할 것다음 확인
volatile counter 불일치복합 연산 원자성 누락count++, CAS 필요성
Atomic 값은 맞지만 상태 불일치여러 변수 불변식 누락immutable snapshot, lock
ConcurrentMap 중복 초기화get-then-put racecomputeIfAbsent
Value 객체 값이 흔들림mutable value thread-unsafevalue 보호 전략

volatile Atomic Concurrent Collection에서는 핵심 신호와 보조 지표를 같은 시간축에서 맞춘다. 먼저 변한 신호와 뒤따라 포화된 신호를 분리해야 원인과 결과를 구분할 수 있다.

장애 상황에서 보는 순서

  1. 필요한 보장이 가시성인지 원자성인지 자료구조 안전성인지 나눈다.
  2. volatile 변수가 복합 연산에 쓰이는지 확인한다.
  3. Atomic 여러 개가 함께 맞아야 하는 상태를 나눠 들고 있는지 본다.
  4. Concurrent collection의 value 객체가 mutable인지 확인한다.
  5. 단일 JVM 내부 보장인지 다중 인스턴스 전체 보장인지 분리한다.

이 순서는 volatile Atomic Concurrent Collection 후보를 빠르게 좁히기 위한 기본 루틴이다. 결론을 내리기 전에는 애플리케이션 증거와 JVM/OS 증거를 최소 한 번 이상 맞춘다.

실전 팁

  • volatile을 쓰기 전에는 필요한 것이 최신 값 관찰인지 복합 연산 원자성인지 묻는다.
  • Atomic counter는 metric성 숫자에 적합하지만 여러 필드 불변식에는 부족하다.
  • Concurrent collection을 쓸 때는 get-then-put 대신 atomic operation을 우선한다.
  • Value 객체가 mutable이면 collection이 안전해도 내부 상태는 별도 보호가 필요하다.
  • 장애 회고에는 어떤 보장이 필요했고 어떤 도구가 그 보장을 제공했는지 적는다.

주니어 팁

  • volatile은 원자성을 주지 않는다는 점을 count++ 예시로 확인한다.
  • Atomic은 “하나의 변수”에 강하고, 여러 값을 함께 맞추는 문제와 다르다.
  • ConcurrentHashMap의 value가 mutable이면 그 value의 thread-safe도 따로 봐야 한다.

시니어 팁

  • In-memory Atomic counter가 여러 인스턴스에서 합산되어야 하면 metric backend나 DB/Redis 전략을 정한다.
  • Concurrent collection은 임시 cache인지 권위 있는 상태 저장소인지 설계에서 구분한다.
  • 복합 불변식은 Atomic 여러 개보다 immutable snapshot이나 명확한 lock 경계가 더 안전할 수 있다.

Guru급 팁

  • volatile Atomic Concurrent Collection 주제는 단일 snapshot보다 변화 방향이 중요하다. 같은 명령을 간격을 두고 실행해 상태가 증가, 유지, 회복 중 어디인지 본다.
  • 고급 도구는 volatile Atomic Concurrent Collection 가설을 좁힐 때만 사용하고 기본 지표와 모순되는지 먼저 확인한다.
  • volatile Atomic Concurrent Collection 조치 후에는 CPU, memory, socket, disk, pool 중 다음 포화 지점이 어디로 이동했는지 확인한다.

위험 신호!

  • volatile int countcount++ race를 해결했다고 생각한다.
  • Atomic 여러 개로 서로 맞아야 하는 상태를 나눠 저장한다.
  • ConcurrentHashMap에 mutable value를 넣고 value 변경을 보호하지 않는다.
  • get-then-put 패턴으로 동일 key 초기화를 중복 수행한다.
  • JVM 내부 Atomic 값으로 다중 인스턴스 전체 정합성을 보장한다고 생각한다.

확인 질문

확인 질문

  • volatile이 보장하는 것과 보장하지 않는 것은 무엇인가?
    • 최신 값 가시성은 돕지만 count++ 같은 복합 연산의 원자성은 보장하지 않는다.
  • Atomic이 적합한 경우와 부족한 경우는 어떻게 나뉘는가?
    • 단일 변수 원자 갱신에는 적합하지만 여러 필드의 불변식을 함께 지켜야 하는 경우에는 부족하다.
  • Concurrent collection을 써도 race가 남을 수 있는 이유는 무엇인가?
    • Collection 내부 구조는 안전해도 value 객체의 mutable field와 get-then-act 흐름은 별도 보호가 필요하기 때문이다.

참고 문서