synchronized 동작 원리
Java의 synchronized 키워드는 메서드 또는 코드 블록에 적용되어 다중 스레드 환경에서 한 번에 하나의 스레드만 접근할 수 있도록 보장합니다.
메서드 동기화
class SyncExample {
public synchronized void syncMethod() {
// 동기화 로직
}
}
// 인스턴스 메서드 동등 표현
class SyncExample {
public void syncMethod() {
synchronized(this) {
// 동기화 로직
}
}
}
// 정적 메서드 동기화
class SyncExample {
public static synchronized void staticSync() {
// 동기화 로직
}
}
// 정적 메서드 동등 표현
class SyncExample {
public static void staticSync() {
synchronized(SyncExample.class) {
// 동기화 로직
}
}
}
객체 동기화 예제
class Counter {
private static int total = 0;
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread adder = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
synchronized (lock) {
total++;
}
}
});
Thread subtractor = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
synchronized (lock) {
total--;
}
}
});
adder.start();
subtractor.start();
adder.join();
subtractor.join();
System.out.println("Result: " + total);
}
}
편향 락(Biased Lock)
경쟁이 없는 초기 상태에서 적용되는 최적화 기법입니다. 첫 CAS 연산으로 스레드 ID를 객체 헤더에 저장한 후, 동일 스레드 접근 시 추가 작업 없이 접근이 허용됩니다.
- 객체 생성 시 편향 락 활성화: Mark Word(0x05)
- 기본 지연 시간 존재(
-XX:BiasedLockingStartupDelay=0으로 비활성화 가능) hashCode()호출 시 편향 락 해제- 다른 스레드의 객체 사용 시 경량 락으로 전환
- 대기/알림 메서드(
wait()/notify()) 호출 시 무게 락으로 전환
경량 락(Lightweight Lock)
스레드 간 경쟁이 시간적으로 겹치지 않는 경우 사용됩니다.
- 스레드 스택에 Lock Record 생성
- CAS로 객체 Mark Word 업데이트 시도
- 성공 시 객체 헤더에 락 기록(00 상태)
- 실패 시 경쟁 발생(락 확장) 또는 재진입
- 잠금 해제 시 CAS로 Mark Word 복원
락 확장(Lock Inflation)
경량 락 경쟁 발생 시 무게 락(Heavyweight Lock)으로 전환됩니다.
- Monitor 객체 생성 및 연결
- 경쟁 스레드는 EntryList에서 차단(BLOCKED)
- 원본 스레드 해제 시 Monitor의 Owner 초기화 및 대기 스레드 깨움
스핀 최적화(Spin Optimization)
무게 락 경쟁 시 CPU 회전으로 차단 회피를 시도합니다.
- 단일 코어 CPU에서는 비효율적
- Java 6 이후 적응형 스핀 구현
- 성공 경험에 따라 스핀 횟수 자동 조정
- Java 7+에서는 사용자 제어 불가
락 업그레이드 과정
무경쟁 → 편향 락 → (경쟁 발생 시) 경량 락 → 무게 락