F-LAB JAVA · 4주차 · Phase 4 · 동기화: synchronized와 메모리 가시성
★ 마스터 Unit + 🏆 Phase 4 완주 — ★ 4주차 1차 정점 완료
이 Unit을 끝내면 다음을 답할 수 있어야 한다.
메모리 가시성 (Visibility) 문제는 한 스레드의 변경이 다른 스레드에 보이지 않는 현상이며, 각 CPU 코어가 자신의 캐시 메모리를 사용하기 때문에 발생한다.
예를 들어 한 스레드가runFlag = false로 변경해도 다른 스레드의 캐시에는 여전히true가 남아while (runFlag)루프가 영원히 종료되지 않을 수 있다.
volatile키워드 는 해당 변수의 읽기·쓰기를 항상 메인 메모리에 직접 수행하도록 하여 (캐시 우회) 가시성을 보장한다 — 약간 느려지지만 변경이 즉시 모든 스레드에 보인다.
단, volatile 은 원자성을 보장하지 않는다 —count++는 여전히 읽기-수정-쓰기 3단계이므로 가시성은 보장되어도 경쟁 조건 (lost update) 은 발생한다.
volatile 은 가시성만, synchronized 는 가시성 + 원자성 을 보장하므로, 단순 플래그 (boolean) 에는 volatile, 복합 연산 (count++) 에는 synchronized/Atomic 을 사용한다.
가시성 문제 = 각자 수첩에 적기:
메인 메모리 = 중앙 게시판
CPU 캐시 = 각자 개인 수첩
문제:
- 스레드 A: 게시판 보고 수첩에 "운영중=true" 적음
- 스레드 B: 게시판에 "운영중=false" 적음
- 하지만 A 는 자기 수첩 (true) 만 봄
- A 는 영원히 "운영중" 으로 착각
volatile = 항상 게시판 직접 확인:
- 수첩 안 씀
- 매번 게시판 (메인 메모리) 읽기/쓰기
- 변경 즉시 모두 봄
원자성 X:
- 게시판 직접 봐도
- "읽고-더하고-쓰기" 사이 다른 사람 개입 가능
- count++ 손실
→ volatile = 항상 메인 메모리 직접 (가시성), 단 원자성 X.
1. 메모리 가시성 문제
2. runFlag 무한 루프 시나리오
3. CPU 캐시와 메인 메모리
4. volatile의 효과
5. volatile은 원자성 보장 X
6. count++가 volatile로도 위험
7. volatile vs synchronized
8. happens-before와 적절한 사용
9. 면접 + 자기 점검 + 마스터 50문항 + Phase 4 완주
메모리 가시성 (Visibility) 문제:
한 스레드의 변경이
다른 스레드에 보이지 않는 현상.
원인:
- 각 CPU 코어가 자기 캐시
- 변경이 캐시에만 (메인 메모리 X)
- 다른 코어는 옛 값
가시성 문제 발생:
스레드 A (코어 1):
변수 x = 10 으로 변경
→ 코어 1 캐시에 10
→ 메인 메모리 반영 지연
스레드 B (코어 2):
변수 x 읽기
→ 코어 2 캐시의 옛 값 (예: 0)
→ A 의 변경 못 봄
가시성 문제:
메인 메모리
x = 0 (옛 값)
/ \
코어1 캐시 코어2 캐시
x = 10 x = 0
(A 변경) (B 가 봄)
↑ ↑
스레드 A 스레드 B
A 변경이 B 에 안 보임
가시성 문제의 또 다른 원인 — 최적화:
컴파일러/JIT 최적화:
- 반복 읽는 변수를 레지스터에 캐싱
- 매번 메모리 안 읽음
- 변경 감지 못함
예:
while (flag) { }
→ flag 를 레지스터에 (한 번 읽고 재사용)
→ flag 변경 감지 X
→ 무한 루프
가시성 vs 원자성:
가시성 (Visibility):
- 변경이 보이는가
- volatile 해결
원자성 (Atomicity):
- 연산 분리 안 됨
- synchronized/Atomic 해결
count++ 는 둘 다 문제:
- 가시성 (변경 안 보임)
- 원자성 (3단계)
public class VisibilityProblem {
private boolean running = true; // 가시성 문제
public void start() {
Thread worker = new Thread(() -> {
while (running) { // 캐시의 옛 값 (true) 만 봄
process();
}
// running = false 변경을 못 볼 수 있음
// → 영원히 루프
});
worker.start();
sleep(1000);
running = false; // 변경 (worker 가 못 볼 수 있음)
}
private void process() { }
}
메모리 가시성 문제의 정의는?
답:
1. 정의:
원인:
메커니즘:
vs 원자성:
public class StopThread {
private boolean runFlag = true; // ★ 가시성 문제
public void start() throws InterruptedException {
Thread worker = new Thread(() -> {
int count = 0;
while (runFlag) { // 캐시의 true 만
count++;
}
System.out.println("Stopped: " + count);
});
worker.start();
Thread.sleep(1000);
runFlag = false; // 변경
System.out.println("Set runFlag = false");
// worker 가 멈출까?
// → 멈추지 않을 수 있음 (가시성 문제)
// → 무한 루프
}
}
무한 루프 이유:
1. CPU 캐시:
- worker 가 runFlag 를 캐시에
- main 의 false 변경이 캐시 미반영
- worker 는 true 계속 봄
2. 컴파일러 최적화:
- while(runFlag) 에서
- runFlag 를 레지스터에 캐싱
- 매번 안 읽음
- 변경 감지 X
→ 멈추지 않음
무한 루프:
main: runFlag = false (메인 메모리)
↓ (worker 캐시 미반영)
worker: while(runFlag) ← 캐시의 true
while(true) ← 영원히
while(true)
...무한...
public class StopThreadFixed {
private volatile boolean runFlag = true; // ★ volatile
public void start() throws InterruptedException {
Thread worker = new Thread(() -> {
while (runFlag) { // 항상 메인 메모리
// ...
}
System.out.println("Stopped");
});
worker.start();
Thread.sleep(1000);
runFlag = false; // 메인 메모리에 즉시
// worker 가 즉시 봄 → 멈춤
}
}
가시성 문제의 비결정성:
같은 코드 실행:
- 어떤 환경: 멈춤 (운 좋게)
- 다른 환경: 무한 루프
의존 요소:
- JVM 구현
- CPU 아키텍처
- 최적화 수준
- 부하
→ "내 PC 에선 됐는데" 함정
→ volatile 로 확실히
@Service
public class ShipmentWorkerControl {
// ❌ 가시성 문제
private boolean stopRequested = false;
// ✓ volatile
private volatile boolean stopRequestedSafe = false;
public void startWorker() {
Thread worker = new Thread(() -> {
while (!stopRequestedSafe) { // volatile (즉시 반영)
processNextShipment();
}
log.info("Worker stopped gracefully");
});
worker.start();
}
public void requestStop() {
stopRequestedSafe = true; // 즉시 worker 에 보임
}
private void processNextShipment() { }
}
runFlag가 영원히 종료 안 되는 시나리오는?
답:
1. 시나리오:
이유:
비결정성:
해결:
메모리 계층 (속도 vs 크기):
레지스터 (가장 빠름)
↓
L1 캐시 (~1ns)
↓
L2 캐시 (~4ns)
↓
L3 캐시 (~10ns, 공유)
↓
메인 메모리 (~100ns)
- 캐시: 코어별 (L1, L2)
- 메인 메모리: 공유
캐시의 목적:
메인 메모리 접근은 느림 (~100ns).
자주 쓰는 데이터를 캐시에 (빠름).
효과:
- 성능 ↑
- 메모리 접근 줄임
문제:
- 코어별 캐시
- 일관성 (가시성)
코어별 캐시:
코어1: L1, L2 (자기 것)
코어2: L1, L2 (자기 것)
공유: L3, 메인 메모리
문제:
- 코어1 이 변수 변경 → 자기 캐시
- 코어2 는 자기 캐시 (옛 값)
- 불일치 (가시성 문제)
캐시 일관성 (Cache Coherence):
MESI 프로토콜 등:
- Modified, Exclusive, Shared, Invalid
- 캐시 라인 상태 관리
하지만:
- 즉시 반영 보장 X (성능)
- 메모리 배리어 필요
- volatile 이 배리어 삽입
메모리 배리어 (Memory Barrier):
CPU 명령 재정렬과 캐시 동기화 제어.
volatile:
- 쓰기 후 store 배리어 (flush)
- 읽기 전 load 배리어 (refresh)
- 메인 메모리 직접
효과:
- 가시성 보장
- 재정렬 방지
public class CacheExample {
// 일반 변수 — 캐시 가능
private int normalCounter = 0;
// volatile — 메인 메모리 직접
private volatile int volatileCounter = 0;
private volatile boolean flag = false;
public void demonstrate() {
// normalCounter: 캐시될 수 있음 (가시성 X)
normalCounter++;
// volatileCounter: 항상 메인 메모리 (가시성 O)
// 단, ++ 는 원자성 X (다음 섹션)
// flag: 가시성 보장 (단순 플래그에 적합)
flag = true; // 즉시 모든 스레드에
}
}
CPU 캐시와 메인 메모리의 관계는?
답:
1. 계층:
목적:
문제:
해결:
private volatile boolean flag = false;
private volatile int counter = 0;
// volatile 변수:
// - 읽기/쓰기 항상 메인 메모리
// - 캐시 우회
// - 가시성 보장
volatile 의 두 가지 효과:
1. 가시성 (Visibility)
- 변경이 즉시 모든 스레드에
- 메인 메모리 직접
2. 순서 보장 (Ordering)
- 재정렬 방지 (메모리 배리어)
- happens-before
volatile 가시성:
쓰기:
- volatile 변수 쓰기
- 즉시 메인 메모리 flush
- 캐시 무효화
읽기:
- volatile 변수 읽기
- 메인 메모리에서 직접
- 최신 값 보장
// 재정렬 문제
public class Reordering {
private int x = 0;
private boolean ready = false;
// 스레드 A
public void writer() {
x = 42; // (1)
ready = true; // (2)
// 재정렬 가능: (2) 가 (1) 보다 먼저?
}
// 스레드 B
public void reader() {
if (ready) { // (3)
System.out.println(x); // (4): 42? 0?
// 재정렬되면 0 가능
}
}
}
// volatile 로 해결
private volatile boolean ready = false;
// ready 쓰기 전 모든 쓰기가 먼저 (happens-before)
volatile 의 비용:
- 캐시 우회 (메인 메모리 접근)
- 메모리 배리어
- 약간 느림 (일반 변수보다)
하지만:
- synchronized 보다 가벼움
- 락 없음
- 단순 가시성에 적합
@Service
public class VolatileEffects {
// 1. 플래그 (가시성)
private volatile boolean shutdownRequested = false;
public void requestShutdown() {
shutdownRequested = true; // 즉시 보임
}
public boolean isShutdownRequested() {
return shutdownRequested; // 최신 값
}
// 2. 상태 변수 (가시성)
private volatile ServiceStatus status = ServiceStatus.STARTING;
public void updateStatus(ServiceStatus newStatus) {
status = newStatus; // 즉시 모든 스레드에
}
// 3. 설정 참조 (불변 객체 + volatile)
private volatile ShipmentConfig config;
public void reloadConfig(ShipmentConfig newConfig) {
config = newConfig; // 원자적 참조 교체 + 가시성
}
enum ServiceStatus { STARTING, RUNNING, STOPPING }
}
volatile의 효과는?
답:
1. 가시성:
순서 보장:
동작:
비용:
volatile 은 원자성 보장 X:
가시성만 보장.
원자성은 보장 안 함.
따라서:
- 단순 읽기/쓰기: 안전
- 복합 연산 (count++): 위험
private volatile int count = 0;
public void increment() {
count++; // ★ volatile 이어도 위험!
// 1. 읽기 (메인 메모리 — 가시성 O)
// 2. 더하기
// 3. 쓰기 (메인 메모리 — 가시성 O)
// 하지만 1~3 사이 다른 스레드 개입 (원자성 X)
}
volatile count++ 위험:
volatile 은 각 읽기/쓰기를 메인 메모리로.
하지만 read-modify-write 전체를 원자적으로 X.
스레드 A: 읽기 (0, 메인 메모리)
스레드 B: 읽기 (0, 메인 메모리) ← A 아직 안 씀
A: +1 → 쓰기 (1)
B: +1 → 쓰기 (1) ← 손실
가시성은 O (둘 다 메인 메모리)
하지만 원자성 X (손실)
public class VolatileNotAtomic {
private volatile int count = 0;
public static void main(String[] args) throws InterruptedException {
VolatileNotAtomic demo = new VolatileNotAtomic();
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Thread t = new Thread(() -> {
for (int j = 0; j < 10000; j++) {
demo.count++; // volatile 이어도 손실
}
});
t.start();
threads.add(t);
}
for (Thread t : threads) t.join();
// 기대: 100,000
// 실제: < 100,000 (volatile 이어도 손실!)
System.out.println("Count: " + demo.count);
}
}
// ❌ volatile (원자성 X)
private volatile int count = 0;
public void inc() { count++; } // 손실
// ✓ AtomicInteger (원자성 O)
private final AtomicInteger atomicCount = new AtomicInteger();
public void incAtomic() { atomicCount.incrementAndGet(); }
// ✓ synchronized (원자성 O)
private int syncCount = 0;
public synchronized void incSync() { syncCount++; }
@Service
public class VolatileAtomicityExample {
// ❌ volatile 카운터 (원자성 X)
private volatile int wrongCounter = 0;
public void incrementWrong() {
wrongCounter++; // 손실 가능
}
// ✓ volatile 플래그 (단순 쓰기, 안전)
private volatile boolean active = false;
public void setActive(boolean value) {
active = value; // 단순 쓰기 (원자적)
}
// ✓ AtomicInteger (복합 연산)
private final AtomicInteger correctCounter = new AtomicInteger();
public void incrementCorrect() {
correctCounter.incrementAndGet(); // 원자적
}
// 정리:
// - 단순 플래그/참조: volatile
// - 증감/복합: Atomic 또는 synchronized
}
volatile이 원자성을 보장하지 않는 이유는?
답:
1. 핵심:
count++ 위험:
시나리오:
해결:
가시성과 원자성 분리:
volatile count++:
- 가시성: O (메인 메모리)
- 원자성: X (3단계)
→ 가시성 있어도 손실
핵심:
- 두 개념 별개
- volatile 은 가시성만
volatile count++ 단계:
1. count 읽기 (메인 메모리 — 가시성 O)
2. +1 (레지스터)
3. count 쓰기 (메인 메모리 — 가시성 O)
문제:
- 1과 3 사이 시간
- 다른 스레드가 1~3 실행
- 둘 다 같은 값 읽음
- 원자성 X
volatile count++ 손실:
스레드 A 스레드 B 메인 메모리
읽기(0) 0
읽기(0) 0 ← 둘 다 0
+1 (1)
+1 (1)
쓰기(1) 1
쓰기(1) 1 ← A의 1 덮어씀
가시성 O (메인 메모리 읽기/쓰기)
하지만 손실 (원자성 X)
// volatile 단순 쓰기 (안전)
private volatile boolean flag;
flag = true; // 단일 쓰기 (원자적 + 가시성)
private volatile int value;
value = 42; // 단일 쓰기 (안전)
// volatile 복합 연산 (위험)
value++; // RMW (위험)
value += 5; // RMW (위험)
value = value * 2; // RMW (위험)
volatile 적합:
✓ boolean 플래그
✓ 단순 상태 변수
✓ 참조 교체 (불변 객체)
✓ 한 스레드만 쓰고 여러 스레드 읽기
volatile 부적합:
✗ 카운터 (count++)
✗ 복합 연산
✗ 여러 변수 일관성
✗ check-then-act
@Service
public class VolatileUsageGuide {
// ✓ 적합 — 플래그
private volatile boolean running = true;
// ✓ 적합 — 상태 (단일 쓰기)
private volatile String currentStatus = "IDLE";
// ✓ 적합 — 참조 교체
private volatile ShipmentConfig config;
// ❌ 부적합 — 카운터 (RMW)
// private volatile int count; // count++ 위험
private final AtomicInteger count = new AtomicInteger(); // 대신 Atomic
public void updateStatus(String status) {
currentStatus = status; // ✓ 단일 쓰기 (안전)
}
public void increment() {
count.incrementAndGet(); // ✓ Atomic (volatile 대신)
}
public void stop() {
running = false; // ✓ 플래그 (안전)
}
}
count++가 volatile로도 위험한 이유는?
답:
1. 가시성 ≠ 원자성:
단계:
적합:
부적합:
| 항목 | volatile | synchronized |
|---|---|---|
| 가시성 | O | O |
| 원자성 | X | O |
| 상호 배제 | X | O |
| 락 | 없음 | 모니터 락 |
| 성능 | 빠름 | 느림 (락) |
| 블로킹 | X | O (BLOCKED) |
| 용도 | 단순 플래그 | 복합 연산 |
volatile vs synchronized 한 문장:
volatile 은 가시성만 보장하고 (락 없음, 빠름),
synchronized 는 가시성 + 원자성 + 상호 배제를 보장한다 (락, 느림).
선택:
- 단순 플래그/참조: volatile
- 복합 연산: synchronized
volatile 의 장점:
- 락 없음 (블로킹 X)
- 빠름
- 데드락 위험 X
- 단순 가시성에 적합
한계:
- 원자성 X
- 상호 배제 X
synchronized 의 장점:
- 가시성 + 원자성
- 상호 배제
- 복합 연산 안전
비용:
- 락 (BLOCKED)
- 성능
- 데드락 위험
선택 가이드:
volatile:
✓ boolean 플래그 (stop, ready)
✓ 단순 상태
✓ 참조 교체 (불변 객체)
synchronized:
✓ 복합 연산 (count++)
✓ 여러 변수 일관성
✓ check-then-act
Atomic:
✓ 단일 변수 원자 연산
✓ count++ (lock-free)
동시성 컬렉션:
✓ Map, List, Queue
@Service
public class VolatileVsSynchronized {
// volatile — 플래그 (가시성)
private volatile boolean active = true;
public void deactivate() {
active = false; // 락 없이 가시성
}
// synchronized — 복합 연산 (원자성)
private int balance = 0;
private final List<Transaction> history = new ArrayList<>();
public synchronized void transfer(int amount) {
balance += amount; // 원자성 + 가시성
history.add(new Transaction(amount)); // 일관성
}
// Atomic — 단일 카운터 (lock-free)
private final AtomicInteger counter = new AtomicInteger();
public void count() {
counter.incrementAndGet(); // 원자적, 빠름
}
record Transaction(int amount) {}
}
volatile vs synchronized 차이는?
답:
1. 한 문장:
volatile:
synchronized:
선택:
happens-before:
Java Memory Model (JMM) 의 규칙.
A happens-before B 면:
- A 의 결과가 B 에 보임
- 순서 보장
규칙:
- 프로그램 순서
- volatile 쓰기 → 읽기
- 락 해제 → 획득
- Thread.start → run
- run 종료 → join
volatile happens-before:
volatile 변수 쓰기
happens-before
이후 그 변수 읽기
효과:
- 쓰기 전 모든 작업이
- 읽기 후 보임
예:
x = 42; // (1)
volatileFlag = true; // (2) volatile 쓰기
// 다른 스레드:
if (volatileFlag) // (3) volatile 읽기
print(x); // (4): 42 보장 (happens-before)
// Double-Checked Locking (volatile 필수)
public class Singleton {
private static volatile Singleton instance; // ★ volatile
public static Singleton getInstance() {
if (instance == null) { // 1차 (락 없이)
synchronized (Singleton.class) {
if (instance == null) { // 2차 (락 안)
instance = new Singleton(); // volatile 쓰기
}
}
}
return instance;
}
// volatile 없으면:
// - 부분 초기화 객체 노출 가능
// - 재정렬 문제
}
// 1. 종료 플래그
private volatile boolean shutdown = false;
// 2. 일회성 발행 (불변 객체)
private volatile Config config; // 참조 교체
// 3. 독립적 관찰 (한 쓰기, 여러 읽기)
private volatile long lastUpdateTime;
// 4. DCL 싱글톤
private static volatile Instance instance;
// 5. 상태 플래그
private volatile State currentState;
// ❌ 카운터
private volatile int count; // count++ 위험
// ❌ 여러 변수 일관성
private volatile int x, y; // x, y 함께 변경 시 불일치
// ❌ check-then-act
private volatile boolean initialized;
if (!initialized) { // 확인
initialized = true; // 행동 (사이 개입)
init();
}
@Service
public class VolatileBestPractices {
// ✓ 종료 플래그
private volatile boolean running = true;
// ✓ 설정 참조 (불변 객체 교체)
private volatile ShipmentConfig config = ShipmentConfig.defaults();
// ✓ 마지막 갱신 시각 (독립 관찰)
private volatile long lastProcessedTime;
public void process(Shipment shipment) {
while (running) { // 가시성
doProcess(shipment);
lastProcessedTime = System.currentTimeMillis(); // 단일 쓰기
}
}
public void reloadConfig(ShipmentConfig newConfig) {
config = newConfig; // 원자적 참조 교체 + happens-before
// config 의 모든 필드가 다른 스레드에 보임
}
public void stop() {
running = false; // 즉시 보임
}
private void doProcess(Shipment s) { }
}
happens-before와 적절한 사용은?
답:
1. happens-before:
volatile:
적절:
부적절:
| Q | 핵심 답변 |
|---|---|
| 가시성 문제? | 변경이 안 보임 (캐시) |
| runFlag 무한 루프? | 캐시 옛 값 |
| CPU 캐시? | 코어별, 불일치 |
| volatile 효과? | 메인 메모리 직접 (가시성) |
| volatile 원자성? | 보장 X |
| count++ volatile? | 위험 (RMW) |
| volatile vs sync? | 가시성 vs 가시성+원자성 |
| happens-before? | 결과 보임 보장 |
| DCL volatile? | 부분 초기화 방지 |
| volatile 적합? | 플래그, 참조 |
Q1. 가시성 문제? → 변경 안 보임
Q2. 원인? → CPU 캐시
Q3. runFlag? → 무한 루프
Q4. 캐시 옛 값? → 변경 미반영
Q5. 컴파일러 최적화? → 레지스터 캐싱
Q6. 비결정성? → 환경마다
Q7. 메인 메모리? → 공유
Q8. 코어 캐시? → 개별
Q9. 가시성 vs 원자성? → 보임 vs 분리
Q10. MESI? → 캐시 일관성
Q11. 메모리 배리어? → 동기화
Q12. 해결? → volatile
Q13. volatile? → 메인 메모리 직접
Q14. 효과 1? → 가시성
Q15. 효과 2? → 순서 (재정렬 방지)
Q16. 쓰기? → flush
Q17. 읽기? → 최신
Q18. 캐시? → 우회
Q19. 비용? → 약간 느림
Q20. 락? → 없음
Q21. 블로킹? → X
Q22. 재정렬? → 방지
Q23. happens-before? → 쓰기→읽기
Q24. 단순 쓰기? → 안전
Q25. 단순 읽기? → 안전
Q26. volatile 원자성? → 보장 X
Q27. count++ volatile? → 위험
Q28. 이유? → RMW 3단계
Q29. 가시성 O? → 각 읽기/쓰기 메인
Q30. 원자성 X? → 전체 비원자
Q31. 손실? → 둘 다 읽고 덮어씀
Q32. 단일 쓰기? → 안전 (flag=true)
Q33. += 위험? → RMW
Q34. 카운터? → Atomic
Q35. 복합 연산? → synchronized
Q36. check-then-act? → 위험
Q37. long/double? → volatile 로 원자적 (64비트)
Q38. volatile vs sync? → 가시성 vs +원자성
Q39. volatile 장점? → 빠름, 락 X
Q40. sync 장점? → 원자성, 상호 배제
Q41. volatile 적합? → 플래그
Q42. sync 적합? → 복합
Q43. Atomic? → 단일 변수 원자
Q44. happens-before 규칙? → JMM
Q45. volatile happens-before? → 쓰기→읽기
Q46. DCL? → volatile 필수
Q47. 부분 초기화? → volatile 방지
Q48. 참조 교체? → volatile 적합
Q49. 종료 플래그? → volatile
Q50. 여러 변수? → synchronized
50 / 50 → volatile 마스터
45-49 → 거의 마스터
40-44 → 복습
< 40 → Unit 4.5 재학습
Phase 4 — synchronized & volatile (★ 1차 정점)
Unit 4.1 — 임계 영역과 동기화의 필요성
- 임계 영역, count++ 3단계
- 경쟁 조건, 원자성
Unit 4.2 — synchronized 메서드
- this 락, Class 락
- 한 번에 하나
Unit 4.3 — synchronized 블록
- 범위 최소화
- 잠금 대상 명시
Unit 4.4 — 모니터 락 (★ 마스터)
- intrinsic lock
- BLOCKED, 공정성 X
- synchronized 단점
Unit 4.5 — volatile (★ 마스터)
- 가시성
- 원자성 X
- vs synchronized
Phase 4 핵심 통찰 5가지:
1. count++ 는 3단계 (비원자)
- 경쟁 조건, 손실
2. synchronized 로 원자성
- this/Class 락
- 한 번에 하나
3. 모니터 락
- 모든 객체
- 공정성 X, 단점 3가지
4. volatile 은 가시성만
- 원자성 X
- count++ 위험
5. 도구 선택
- 플래그: volatile
- 복합: synchronized/Atomic
✅ Phase 1 — 동시성의 기초 (4 Unit)
✅ Phase 2 — 4분면 매트릭스 (3 Unit)
✅ Phase 3 — 스레드 다루기 (5 Unit)
✅ Phase 4 — synchronized & volatile (5 Unit) ★ 1차 정점 ← 완주
⏭ Phase 5 — Lock 도구 (4 Unit)
⏭ Phase 6 — 스레드 협력 (4 Unit)
⏭ Phase 7 — Executor (7 Unit) ★ 2차 정점
⏭ Phase 8 — 고급 비동기 (3 Unit)
총: 17/35 Unit (Phase 4 완주, 약 49%)
답:
답:
답:
답:
답:
1. 가시성과 volatile
2. 원자성 X
3. 선택
🚀 Phase 4 — synchronized & volatile (★ 1차 정점)
✅ Unit 4.1 임계 영역과 동기화의 필요성
✅ Unit 4.2 synchronized 메서드
✅ Unit 4.3 synchronized 블록
✅ Unit 4.4 모니터 락 (★ 마스터)
✅ Unit 4.5 volatile (★ 마스터) ← 여기, Phase 4 완주
→ 임계 영역 + 원자성
→ synchronized (this/Class/모니터 락)
→ volatile (가시성)
→ ★ 4주차 1차 정점 완료
Phase 5 — Lock 도구 (4 Unit)
Unit 5.1 — synchronized의 한계 정리
Unit 5.2 — LockSupport (저수준 도구)
Unit 5.3 — ReentrantLock (실무 표준)
Unit 5.4 — tryLock으로 데드락 회피 (★ 마스터)
✅ Phase 1 — 동시성의 기초 (4 Unit)
✅ Phase 2 — 4분면 매트릭스 (3 Unit)
✅ Phase 3 — 스레드 다루기 (5 Unit)
✅ Phase 4 — synchronized & volatile (5 Unit) ★ 1차 정점
⏭ Phase 5 — Lock 도구 (4 Unit)
총: 17/35 Unit (약 49%)
★ 마스터 Unit — volatile 완료
🏆 Phase 4 완주 — synchronized & volatile 마스터 (★ 1차 정점 완료)