-
원자성의 정의는 CPU에서 실행 중인 하나 이상의 연산이 중단되지 않는 특성을 의미합니다.
-
동시성 문제의 원인은 스레드 전환입니다.
-
원자성 문제 해결 방법은 CPU 중단을 차단하여 스레드 전환을 방지하는 것입니다.
-
단일 코어 환경에서는 원자성 문제를 쉽게 해결할 수 있지만, 멀티코어 환경에서는 관리가 복잡합니다.
-
32비트 CPU에서 long형 변수 작성 시 두 번의 쓰기 작업(상위 32비트와 하위 32비트)으로 분할됩니다.
-
뮤텍스는 동일 시점에 하나의 스레드만 실행되는 것을 의미합니다.
-
원자성 보장을 위한 핵심은 공유 변수 수정을 상호 배타적으로 처리하는 것입니다.
-
간단한 뮤텍스 모델은 다음과 같습니다.
-
기본 뮤텍스 모델의 한계는 락이 무엇인지, 보호하는 자원이 무엇인지 명확하지 않다는 점입니다.
-
모델 개선을 위한 접근 방법은 락과 보호 자원의 관계를 명확히 하는 것입니다. 예를 들어, 가정의 자산을 보호하는 데 가정의 자물쇠를 사용하는 것과 같은 원리입니다.
-
개선된 뮤텍스 모델은 다음과 같은 구조를 갖습니다.
-
개선된 모델에서 주의할 점은 락과 보호 자원 간의 연관성을 유지하는 것입니다. 이 관계를 간과하면 동기화 버그가 발생할 수 있습니다.
-
Java에서 제공하는 동기화 기술은 synchronized 키워드입니다.
-
synchronized가 적용 가능한 대상은 메서드와 코드 블록입니다.
-
예제 코드는 다음과 같습니다.
class Example {
// 인스턴스 메서드에 적용
synchronized void increment() {
// 임계영역
}
// 정적 메서드에 적용
synchronized static void counter() {
// 임계영역
}
// 코드 블록에 적용
Object lockObject = new Object();
void process() {
synchronized(lockObject) {
// 임계영역
}
}
}
-
코드에서 락/해제 명령어를 보지 못하는 이유는 Java가 자동으로 처리하기 때문입니다.
-
자동 락 관리의 장점은 락 획득과 해제가 항상 쌍으로 이루어져 오류를 방지하는 것입니다.
-
메서드에 적용된 synchronized의 락 대상은 다음과 같습니다.
- 정적 메서드: 클래스 객체(X.class)
- 인스턴스 메서드: this 객체
- count +=1 문제 해결 예제는 다음과 같습니다.
class SafeCalculator {
long value = 0L;
long get() {
return value;
}
synchronized void addOne() {
value += 1;
}
}
-
이 코드는 원자성 문제를 해결합니다. synchronized로 감싸진 메서드는 단일 스레드만 실행되므로 원자성을 보장합니다.
-
get() 메서드가 addOne()의 변경 사항을 확인하지 못하는 문제는 다음과 같이 해결합니다.
class SafeCalculator {
long value = 0L;
synchronized long get() {
return value;
}
synchronized void addOne() {
value += 1;
}
}
-
뮤텍스와 보호 자원의 관계는 다음과 같습니다. 하나의 락은 여러 자원을 보호할 수 있지만, 하나의 자원은 하나의 락만 가질 수 있습니다.
-
다음 코드는 동기화 문제를 유발합니다. 정적 메서드와 인스턴스 메서드가 서로 다른 락을 사용하기 때문입니다.
class SafeCalculator {
static long value = 0L;
synchronized long get() {
return value;
}
synchronized static void addOne() {
value += 1;
}
}
- 코드 블록에 적용된 synchronized의 문제점은 다음과 같습니다.
- 서로 다른 락 객체 사용
- JVM 최적화로 인해 락이 제거될 수 있음
- 락의 본질은 락 객체 헤드에 현재 스레드 ID를 기록하는 것입니다.