Java 메모리 모델(JMM)을 이해하고 멀티쓰레드 환경에서의 가시성 문제를 해결하는 것은 안정적인 동시성 프로그래밍의 핵심입니다. 이 글에서는 JMM의 심층적인 이해와 가시성 문제 해결 기법을 탐구합니다.
1. Java 메모리 모델 심층 분석
JMM은 멀티쓰레드 프로그램의 동작을 정의합니다. 주요 개념을 살펴봅시다.
public class JMMExample {
private int x = 0;
private int y = 0;
private volatile boolean flag = false;
public void writer() {
x = 1; // 1
y = 2; // 2
flag = true; // 3: volatile 쓰기
}
public void reader() {
if (flag) { // 4: volatile 읽기
System.out.println(x + y); // 5
}
}
}
이 예제에서 volatile 키워드는 메모리 장벽(memory barrier)을 생성하여 가시성을 보장합니다.
2. 가시성 문제 해결 기법
다양한 방법으로 가시성 문제를 해결할 수 있습니다.
public class VisibilitySolutions {
// 1. volatile 키워드 사용
private volatile boolean flag = false;
// 2. AtomicReference 활용
private AtomicReference atomicValue = new AtomicReference<>(0);
// 3. synchronized 블록 사용
private int syncValue = 0;
public synchronized void updateSyncValue(int newValue) {
syncValue = newValue;
}
// 4. Lock 인터페이스 활용
private final Lock lock = new ReentrantLock();
private int lockProtectedValue = 0;
public void updateWithLock(int newValue) {
lock.lock();
try {
lockProtectedValue = newValue;
} finally {
lock.unlock();
}
}
// 5. ThreadLocal 사용
private ThreadLocal threadLocalValue = ThreadLocal.withInitial(() -> 0);
public void updateThreadLocal(int newValue) {
threadLocalValue.set(newValue);
}
}
3. Happens-Before 관계 이해하기
Happens-Before 관계는 JMM에서 메모리 가시성을 보장하는 핵심 개념입니다.
public class HappensBefore {
private int value = 0;
private volatile boolean flag = false;
public void writer() {
value = 42; // 1
flag = true; // 2
}
public void reader() {
if (flag) { // 3
System.out.println(value); // 4
}
}
}
이 예제에서 1 happens before 2, 3 happens before 4, 그리고 2 happens before 3이 보장됩니다.
*실행 우선 순위 1>2>3>4
결론
Java 메모리 모델과 가시성 문제를 깊이 이해하고 적절한 해결 기법을 적용하는 것은 안정적인 멀티쓰레드 프로그래밍의 기초입니다. 다양한 기법을 상황에 맞게 활용하여 동시성 문제를 효과적으로 해결할 수 있습니다.