Java volatile vs static 차이 - 멀티스레드 환경에서의 변수 관리
static은 변수가 어디에 속하냐, volatile은 어디서 읽냐의 문제 — 멀티스레드 환경에서 둘의 역할 차이
Java volatile vs static 차이 - 멀티스레드 환경에서의 변수 관리
static과 volatile은 완전히 다른 개념이다. 멀티스레드 환경에서 변수를 다룰 때 둘의 역할을 정확히 알아야 한다.
핵심 차이
1
2
static → 변수가 어디에 속하냐 (클래스 vs 인스턴스)
volatile → 변수를 어디서 읽냐 (메인 메모리 vs CPU 캐시)
스레드의 메모리 구조
1
2
3
4
5
6
7
8
9
10
11
스레드 A 스레드 B
┌──────────┐ ┌──────────┐
│ Stack A │ │ Stack B │ ← 각자 별도 (지역변수, 매개변수)
│ CPU캐시A│ │ CPU캐시B│ ← 각자 별도 (여기가 문제)
└────┬─────┘ └────┬─────┘
│ │
└──────────┬───────────────┘
┌─────┴─────┐
│ Heap │ ← 공유 (인스턴스 변수, static 변수)
│ Text/Data │ ← 공유 (클래스 정보, 상수)
└───────────┘
- 스택의 지역변수: 스레드마다 별도 →
volatile필요 없음 (공유 안 함) - 힙의 인스턴스/static 변수: 공유하지만, 각 CPU가 캐시에 복사본을 들고 있을 수 있음
volatile이 필요한 이유
힙의 공유 변수를 CPU가 자기 캐시에 복사해두는 게 문제다. 메뉴판이 1개여도 직원들이 각자 메모해서 들고 다니는 것과 같다.
volatile없으면: 스레드 A가 값을 바꿔도, 스레드 B는 자기 CPU 캐시(옛날 값)를 계속 봄volatile있으면: CPU 캐시 무시, 항상 메인 메모리(힙)에서 직접 읽음 → 최신 값 보장
싱글톤과의 관계: 싱글톤 Bean은 힙에 인스턴스 1개 → 모든 스레드가 같은 변수를 공유 → CPU 캐시 불일치 문제 발생 → volatile 필요. 매 요청마다 새 인스턴스를 만드는 구조였다면 변수를 공유하지 않으니 volatile이 필요 없다.
싱글톤인데 상수에 static을 쓰는 이유
싱글톤이면 어차피 인스턴스 1개라 실질적 차이는 없지만:
- 의도 표현 - “이 값은 인스턴스와 무관하다”
- 안전장치 - 스코프가 바뀌어도(프로토타입 등) 안전하게 동작
- 관례 - Logger, 상수는
private static final이 표준
조합별 예시와 판단 기준
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 1. static final — 절대 안 바뀌는 상수
private static final Logger log = LoggerFactory.getLogger(MyService.class);
private static final int MAX_RETRY = 3;
// 2. volatile — 멀티스레드에서 변하는 공유 값
// 일반 클래스: 스레드 간 플래그
public class Worker implements Runnable {
private volatile boolean running = true;
public void run() {
while (running) { doWork(); } // 다른 스레드가 stop() 호출하면 즉시 반영
}
public void stop() { running = false; }
}
// Spring 싱글톤 Bean: 인메모리 캐싱
@Service
public class DashboardService {
private static final long CACHE_TTL_MS = 60 * 60 * 1000L;
private volatile DashboardResponse cachedResponse;
private volatile long cacheTimestamp;
}
// 3. static volatile — 클래스 레벨 접근 + 멀티스레드 공유
public class AppStatus {
private static volatile boolean initialized = false;
public static void init() {
initialized = true; // 모든 스레드가 즉시 true를 봄
}
public static boolean isReady() { return initialized; }
}
핵심: volatile은 Spring Bean 전용이 아니라, 여러 스레드가 하나의 변수를 읽고 쓰는 모든 상황에서 필요하다.
| 판단 기준 | 키워드 |
|---|---|
| 값이 안 바뀜 | static final |
| 값이 바뀜 + 멀티스레드에서 공유 | volatile |
| 값이 바뀜 + 클래스 레벨 접근 + 멀티스레드 | static volatile |
This post is licensed under CC BY 4.0 by the author.