JVM 메모리 모델과 하드웨어 메모리 모델간의 관계
https://takeaction.github.io/Java-Memory-Model/
https://software.rajivprab.com/2018/04/29/myths-programmers-believe-about-cpu-caches/
https://beatmejy.tistory.com/15
https://chunsubyeong.tistory.com/73
속도 기준으로 L1>L2>L3로 L1이가장 빠르다.
일반적으로 L1, L2, L3 캐시는 CPU 내부에 위치하지만 L2 부터는 외부에 위치할 수도 있다.
L1 캐시 (Level 1 Cache):
- L1 캐시는 CPU 코어 내부에 위치하며, 매우 빠른 액세스 시간을 가진다
- 명령 캐시와 데이터 캐시로 나뉜다.
L2 캐시 (Level 2 Cache):
- L2 캐시는 L1 캐시의 바로 다음에 위치하며, L1 캐시보다는 느리지만 메인 메모리보다는 빠른 액세스 시간을 가집니다.
- L1 캐시에서 cache miss가 발생한 경우 사용된다.
L3 캐시 (Level 3 Cache):
- 여러 코어가 공유한다. 따라서 멀티코어 프로세서에서 사용됩니다.
- 주로 멀티코어 CPU에서 캐시 일관성을 유지하고 다수의 코어 간의 데이터 공유를 관리하는 데 사용됩니다.
volatile 이란?
- 동기화 방지 키워드 이다.
- 데이터를 Read할 때마다 CPU cache에 저장된 값이 아닌 Main Memory에서 Read 한다.
- 데이터를 Write할 때마다 Main Memory에 Write 한다.
언제 사용?
- Multi Thread 환경에서 하나의 Thread만 write하고 나머지 Thread가 read하는 상황에서
가장 최신의 값을 보장
합니다.
장점
- 멀티스레드 환경에서 가시성을 보장하여 데이터의 일관성을 유지할 수 있다.
*가시성: 멀티스레딩 환경에서 변수의 변경 사항이 다른 스레드에 즉시 반영되는 것을 의미
단점
- 가시성은 보장하지만 원자성은 보장하지 않는다. (여러 스레드가 동시에 write 하는경우)
- *원자성: 연산이 원자적으로 실행되는 것, 원자적으로 실행된다면 한 스레드가 연산을 수행하는 동안 다른 스레드는 그 연산의 중간 상태를 볼 수 없다.
- 항상 메인 메모리와 동기화 해야 하므로 성능저하가 될 수 있다.
실무에서는?
보통 volatile을 직접 사용하진 않으며 Atomic을 사용한다.
Atomic을 사용하면 멀티스레드 환경에서 read/write를 병행할 수 있으며 원자성을 보장할 수 있다.
자바에서 AtomicInteger 타입을 확인해보면
public class AtomicInteger extends Number implements java.io.Serializable {
...
private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");
private volatile int value;
public final int incrementAndGet() {
return U.getAndAddInt(this, VALUE, 1) + 1;
}
public final boolean compareAndSet(int expectedValue, int newValue) {
return U.compareAndSetInt(this, VALUE, expectedValue, newValue);
}
...
...
incrementAndGet()의 U.getAndAddInt(..)를 확인해보면 CAS가 적용되있는것을 확인할 수 있다.
@HotSpotIntrinsicCandidate
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!weakCompareAndSetInt(o, offset, v, v + delta));
return v;
}
...
@HotSpotIntrinsicCandidate
public final boolean weakCompareAndSetInt(Object o, long offset,
int expected,
int x) {
return compareAndSetInt(o, offset, expected, x);
}
CAS는? Compare And Swap
내가 기존에 들고있던 값과 현재 값이 일치한다면 새로운 값을 할당한다.
이런 과정으로 다른 스레드가 동시에 변수를 수정하거나 변경할 여지가 없다.
현재 값이 기존에 들고있던 값과 일치하지 않으면, 아무 작업도 수행하지 않기 때문이다.
그래서 결국 CAS는 데이터의 원자성을 보장할 수 있게 된다.
CAS예시 코드
public class Atomic_Cas {
int val;
public boolean compareAndSwap(int oldVal, int newVal) {
if(val == oldVal) {
val = newVal;
return true;
} else {
return false;
}
}
}
'Language > 자바' 카테고리의 다른 글
자바 진수변환 엄청 쉽게 하는 방법 (0) | 2024.08.23 |
---|---|
빅엔디안, 리틀엔디안이란? (0) | 2024.06.11 |
HashMap과 Hashtable의 차이점 (0) | 2023.10.02 |
JAVA의 WatchService에서 주의할점 (0) | 2023.08.10 |
Java 8 date/time type `java.time.LocalDateTime` not supported by default 문제 해결 (0) | 2022.11.10 |