
Java 언어에서는 전역 변수를 제어하기 위한 다양한 키워드 및 메서드를 제공해줍니다.
대표적인 예로는 volatile, static 등이 있죠.
Volatile의 경우, 스레드 간에 변수 값을 항상 "최신 상태로" 보게 보장해주는 키워드입니다.
Volatile은 다음과 같은 특징을 가지고 있습니다.
- 가시성 보장: 한 스레드에서 변경한 값을 다른 스레드가 즉시 볼 수 있게 함
- 메모리 캐시 무시: CPU 캐시에 있는 값을 사용하지 않고, 메인 메모리에서 직접 읽고 씀
보통 서버 내부적으로 다수의 스레드가 동작할 때, 변수 값의 일관성을 확보하기 위해 사용하는 방법입니다.
아니 근데.. 정적 키워드 static이 있지않나요..??
static 키워드의 경우, "같은 값을 공유"하지만, "최신 값"은 보장하지 않는다는 특징을 가지고 있습니다.
이 뜻이 무엇이냐, 각각의 스레드에서 static 변수에 대한 CPU 캐시로 값을 보관하지만, 간혹 한 스레드에서 바꾼 값이 다른 스레드에는 보이지 않을 수 있다는 것을 의미합니다.
반면에, volatile 키워드의 경우 CPU 캐시 값을 사용하지 않고 메인 메모리에서 직접 읽고 쓰기 때문에 멀티 스레딩 환경에서 유용하게 사용될 키워드 입니다.
그렇다면, volatile의 경우 어떻게 최신 값을 유지하는 걸까요?
일반적으로 멀티 스레딩 환경에서 메모리 관리는 아래 사진과 같이 스레드 내부에서 메인 메모리와 상호작용하며 관리가 됩니다.

이 구조에 따라 static 변수는 각 스레드에서 메모리를 관리하는 CPU 캐시 관리 기법인 MESI 기법을 통해 관리하지만, volatile의 경우에는 메모리에 직접 읽고 쓰는 방식인 JMM(Java Memory Model) 방식을 사용하여 다음과 같이 사용할 수 있습니다.
- volatile 변수에 쓰는 순간 => 즉시 main memory로 flush됨
- volatile 변수 읽을 때 => 항상 main memory에서 읽음
즉 static 변수는 CPU 캐시인 L1, L2에서 값을 가져오기 때문에 다른 스레드에서 값 변경이 발생할 경우 main memory와 불일치할 수 있다는 특징을 가지고 있습니다.
MESI 방식과 JMM 방식을 비교 해 보고 싶다면, 다음 코드를 통해 확인이 가능합니다. (다만, 환경에 따라 다를 수 있음에 주의..)
// 주석을 지우고 실행
private static /*volatile*/ boolean running = true;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
System.out.println("Worker thread started");
while (running) {
// busy-wait
}
System.out.println("Worker thread stopped");
});
t1.start();
// 1초 후에 running을 false로 바꿈
Thread.sleep(1000);
System.out.println("Main thread setting running = false");
running = false;
// worker thread가 멈출 때까지 기다림
t1.join();
System.out.println("Main thread exited");
}
그렇다면 Volatile은 원자성을 가지는가?
Volatile의 경우 항상 최신 값을 유지하기는 하지만, Race Condition(경합)이 발생하는 환경에서는 원자성을 유지하기 힘들다는 특징을 가지고 있습니다.
그 이유는, volatile 변수의 경우 thread-safe 하지않기 때문에 여러 스레드에서 동시에 값을 읽고 쓰게 된다면 값이 순차적으로 변경되는 것이 아닌 덮어씌워지기 때문이죠.
그렇다면 이 threa-safe 하지도 않은 volatile을 어디에 써야할까요??
연산이 아닌 단일 읽기/쓰기를 위한 곳에 사용이 되는데, 예를들면 HikariCP의 내부 설정 클래스 내부에서 이를 활용하는 것을 알 수 있습니다.
@SuppressWarnings({"SameParameterValue", "unused"})
public class HikariConfig implements HikariConfigMXBean
{
...
// Properties changeable at runtime through the HikariConfigMXBean
//
private volatile String catalog;
private volatile long connectionTimeout;
private volatile long validationTimeout;
private volatile long idleTimeout;
private volatile long leakDetectionThreshold;
private volatile long maxLifetime;
private volatile int maxPoolSize;
private volatile int minIdle;
private volatile String username;
private volatile String password;
...
}
아직까지는 멀티 스레딩 환경에서 위와 같이 사용하지 못 했어서 volatile 키워드에 대해서는 아직 미숙한 점이 많은듯합니다.
'언어 > java' 카테고리의 다른 글
[java] ObjectMaper를 사용한 직렬화 (0) | 2025.03.28 |
---|---|
[시스템 프로그래밍] JVM 실행 흐름에 대한 코드 분석 (0) | 2024.07.17 |
JDK 동적 프록시 (2) | 2023.12.05 |
JDK21 Update 정리 (2) | 2023.11.22 |
[Java] 자바의 컬렉션(Collection) (2) | 2020.05.10 |

Java 언어에서는 전역 변수를 제어하기 위한 다양한 키워드 및 메서드를 제공해줍니다.
대표적인 예로는 volatile, static 등이 있죠.
Volatile의 경우, 스레드 간에 변수 값을 항상 "최신 상태로" 보게 보장해주는 키워드입니다.
Volatile은 다음과 같은 특징을 가지고 있습니다.
- 가시성 보장: 한 스레드에서 변경한 값을 다른 스레드가 즉시 볼 수 있게 함
- 메모리 캐시 무시: CPU 캐시에 있는 값을 사용하지 않고, 메인 메모리에서 직접 읽고 씀
보통 서버 내부적으로 다수의 스레드가 동작할 때, 변수 값의 일관성을 확보하기 위해 사용하는 방법입니다.
아니 근데.. 정적 키워드 static이 있지않나요..??
static 키워드의 경우, "같은 값을 공유"하지만, "최신 값"은 보장하지 않는다는 특징을 가지고 있습니다.
이 뜻이 무엇이냐, 각각의 스레드에서 static 변수에 대한 CPU 캐시로 값을 보관하지만, 간혹 한 스레드에서 바꾼 값이 다른 스레드에는 보이지 않을 수 있다는 것을 의미합니다.
반면에, volatile 키워드의 경우 CPU 캐시 값을 사용하지 않고 메인 메모리에서 직접 읽고 쓰기 때문에 멀티 스레딩 환경에서 유용하게 사용될 키워드 입니다.
그렇다면, volatile의 경우 어떻게 최신 값을 유지하는 걸까요?
일반적으로 멀티 스레딩 환경에서 메모리 관리는 아래 사진과 같이 스레드 내부에서 메인 메모리와 상호작용하며 관리가 됩니다.

이 구조에 따라 static 변수는 각 스레드에서 메모리를 관리하는 CPU 캐시 관리 기법인 MESI 기법을 통해 관리하지만, volatile의 경우에는 메모리에 직접 읽고 쓰는 방식인 JMM(Java Memory Model) 방식을 사용하여 다음과 같이 사용할 수 있습니다.
- volatile 변수에 쓰는 순간 => 즉시 main memory로 flush됨
- volatile 변수 읽을 때 => 항상 main memory에서 읽음
즉 static 변수는 CPU 캐시인 L1, L2에서 값을 가져오기 때문에 다른 스레드에서 값 변경이 발생할 경우 main memory와 불일치할 수 있다는 특징을 가지고 있습니다.
MESI 방식과 JMM 방식을 비교 해 보고 싶다면, 다음 코드를 통해 확인이 가능합니다. (다만, 환경에 따라 다를 수 있음에 주의..)
// 주석을 지우고 실행
private static /*volatile*/ boolean running = true;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
System.out.println("Worker thread started");
while (running) {
// busy-wait
}
System.out.println("Worker thread stopped");
});
t1.start();
// 1초 후에 running을 false로 바꿈
Thread.sleep(1000);
System.out.println("Main thread setting running = false");
running = false;
// worker thread가 멈출 때까지 기다림
t1.join();
System.out.println("Main thread exited");
}
그렇다면 Volatile은 원자성을 가지는가?
Volatile의 경우 항상 최신 값을 유지하기는 하지만, Race Condition(경합)이 발생하는 환경에서는 원자성을 유지하기 힘들다는 특징을 가지고 있습니다.
그 이유는, volatile 변수의 경우 thread-safe 하지않기 때문에 여러 스레드에서 동시에 값을 읽고 쓰게 된다면 값이 순차적으로 변경되는 것이 아닌 덮어씌워지기 때문이죠.
그렇다면 이 threa-safe 하지도 않은 volatile을 어디에 써야할까요??
연산이 아닌 단일 읽기/쓰기를 위한 곳에 사용이 되는데, 예를들면 HikariCP의 내부 설정 클래스 내부에서 이를 활용하는 것을 알 수 있습니다.
@SuppressWarnings({"SameParameterValue", "unused"})
public class HikariConfig implements HikariConfigMXBean
{
...
// Properties changeable at runtime through the HikariConfigMXBean
//
private volatile String catalog;
private volatile long connectionTimeout;
private volatile long validationTimeout;
private volatile long idleTimeout;
private volatile long leakDetectionThreshold;
private volatile long maxLifetime;
private volatile int maxPoolSize;
private volatile int minIdle;
private volatile String username;
private volatile String password;
...
}
아직까지는 멀티 스레딩 환경에서 위와 같이 사용하지 못 했어서 volatile 키워드에 대해서는 아직 미숙한 점이 많은듯합니다.
'언어 > java' 카테고리의 다른 글
[java] ObjectMaper를 사용한 직렬화 (0) | 2025.03.28 |
---|---|
[시스템 프로그래밍] JVM 실행 흐름에 대한 코드 분석 (0) | 2024.07.17 |
JDK 동적 프록시 (2) | 2023.12.05 |
JDK21 Update 정리 (2) | 2023.11.22 |
[Java] 자바의 컬렉션(Collection) (2) | 2020.05.10 |