* 이 포스팅은 김영한님의 자바 고급편을 보고 정리한 내용입니다.
안녕하세요 지난 포스팅까지는 메모리 가시성에 대해서 포스팅을 했습니다.
메모리가 가시성의 정의 부터 시작해서
실제 멀티스레드 환경에서 스레드들이 공유하는 메모리에 어떻게 접근하지는 직접확인했고
캐싱 메모리, 메인 메모리의 개념도 학습했습니다, 그리고 자바의 메모리 가시성 문제까지 확인했습니다.
오늘은 지난 포스팅에 이어 자바에서는 이런 메모리 가시성문제를 어떻게 해결했는지
그리고 어떻게 해결할지에 대해 정리하는 시간을 가져보고자 합니다.
자바의 volatile
지난 포스팅을 통해 자바에서 발생하는 메모리 가시성 근본문제를 파악했습니다
각 스레드마다 같은 메모리를 참조하는것이 아닌 성능향상을 위해
캐싱메모리를 참조했고 이로 인해서 메모리 가시성 문제가 발생했다고 했습니다.
그럼 단순하게 캐싱 메모리를 참조하지 않고 메인 메모리를 참조하면 해결되지 않을까요?
맞습니다..
여기서 트레이드 오프가 발생하는데 성능을 포기하는 대신 가시성 문제를 해결하기위해
메인 메모리를 직접 참조를 한다면 메모리 가시성 문제를 해결할 수 있습니다.
이는 자바에서 간단하게 공유하는 필드값 앞에 volatile 키워드를 추가 해주면 해당 값은 메인 메모리에서만 참조
하고 가시성 문제가 해결됩니다.
package thread.volatile1;
import static util.MyLogger.log;
import static util.ThreadUtils.sleep;
public class VolatileFlagMain {
public static void main(String[] args) {
MyTask task = new MyTask();
Thread t = new Thread(task, "work");
log("runFlag = " + task.runFlag);
t.start();
sleep(1000);
log("runFlag를 false로 변경 시도");
task.runFlag = false;
log("runFlag = " + task.runFlag);
log("main 종료");
}
static class MyTask implements Runnable {
volatile boolean runFlag = true;
@Override
public void run() {
log("task 시작");
while (runFlag) {
// runFlag가 false로 변하면 탈출
}
log("task 종료");
}
}
}
지난 포스팅에서 본 익숙한 코드예제입니다.
기존 코드에서 runFlag 앞에 volatile 이라는 키워드 하나만 추가하면
runFlag에 대해서는 캐시 메모리를 사용하지 않고, 값을 읽거나 쓸 때 항상 메인 메모리에 직접 접근하게 됩니다.
실행결과
15:39:59.830 [ main] runFlag = true
15:39:59.830 [ thread-0] task 시작
15:40:00.837 [ main] runFlag를 false로 변경 시도
15:40:00.838 [ thread-0] task 종료
15:40:00.838 [ main] runFlag = false
15:40:00.838 [ main] main 종료
자바 메모리 모델
메모리 가시성
멀티스레드 환경에서 한 스레드가 변경한 값이 다른 스레드에서 언제 보이는지에 대한 메모리 가시성이라고합니다. 이름 그대로
메모리에 변경한 값이 보이는가, 보이지 않는가의 문제입니다
Java Memory Model
Java Memory Model (JMM)은 자바 프로그램이 어떻게 메모리에 접근하고 수정할 수 있는지를 규정하고,
특히 멀티스레드 프로그래밍에서 스레드 간의 상호작용을 정의합니다. JMM에 여러가지 내용이 있지만, 여러 스레드들의 작업 순서를 보장하는 happens-before 관계에 대한 정의입니다.
Happens-before
happens-before 관계는 이름 그대로, 한 동작이 다른 동작보다 먼저 발생함을 보장합니다.
happens-before 관계는 스레드 간의 메모리 가시성을 보장하는 규칙입니다.
happens-before 관계가 성립하면, 한 스레드의 작업을 다른 스레드에서 볼 수 있게됩니다.
즉, 한 스레드에서 수행한 작업을 다른 스레드가 참조할 때 최신 상태가 보장되는것입니다.
이 규칙을 따르면 프로그래머가 멀티스레드 프로그램을 작성할 때 예상치 못한 동작을 피할 수 있습니다.
다향한 규칙들이 존재하지만
결국 핵심 내용은 다음과 같습니다
volatile 또는 스레드 동기화 기법을 사용하면 메모리 가시성 문제가 발생하지 않는다
이 내용을 기억하시고, 스레드 동기화 기업은 다음 포스팅때 올리도록 하겠습니다
감사합니다.
'OS' 카테고리의 다른 글
동시성 문제 해결 synchronized 메서드 (0) | 2024.09.11 |
---|---|
동기화 - synchronized (동시성 이슈) (0) | 2024.09.10 |
OS 스레드의 예외 처리 (0) | 2024.09.03 |
Java 스레드 Deep Dive (4) | 2024.09.02 |
[컨텍스트 스위칭] 스레드 2번째 이야기 (0) | 2024.08.31 |