4주차 Unit 4.2 — synchronized 메서드

Psj·2026년 5월 21일

F-lab

목록 보기
133/230

Unit 4.2 — synchronized 메서드

F-LAB JAVA · 4주차 · Phase 4 · 동기화: synchronized와 메모리 가시성


📌 학습 목표

이 Unit을 끝내면 다음을 답할 수 있어야 한다.

  • synchronized 메서드 의 정의와 동작은?
  • synchronized 의 잠금 대상 (this) 은?
  • 같은 객체에 한 번에 하나의 스레드만 진입하는 원리는?
  • static synchronized 의 잠금 대상 (Class 객체) 은?
  • 인스턴스 락 vs 클래스 락 의 차이는?
  • 같은 클래스의 다른 synchronized 메서드 둘이 동시 호출 가능한가?
  • synchronized 의 가시성 보장 은?
  • synchronized 의 재진입성 (Reentrant) 은?
  • synchronized 메서드의 단점 은?

🎯 핵심 한 문장

synchronized 메서드는 메서드 시그니처에 synchronized 키워드를 붙여, 같은 객체에 대해 한 번에 하나의 스레드만 진입하도록 보장하는 동기화 방식이다.
인스턴스 메서드의 잠금 대상은 this (해당 인스턴스) 이고, static synchronized 메서드의 잠금 대상은 그 클래스의 Class 객체 다 (인스턴스 무관).
한 스레드가 객체의 synchronized 메서드에 진입하면 그 객체의 모니터 락을 획득하고, 같은 객체의 다른 synchronized 메서드도 다른 스레드가 진입할 수 없다 (같은 락을 공유하기 때문).
synchronized 는 상호 배제뿐 아니라 가시성 도 보장하며 (락 해제 시 변경을 메인 메모리에 flush), 재진입 가능 (Reentrant) 하다 (같은 스레드는 이미 가진 락을 다시 획득 가능).
단점은 메서드 전체가 잠겨 동기화 범위가 넓고 (성능 저하), 잠금 대상을 세밀하게 제어할 수 없다는 점이다 (다음 Unit 의 synchronized 블록이 대안).

비유 — 1인용 화장실 열쇠

synchronized 메서드 = 화장실 열쇠 1개:

인스턴스 락 (this):
  - 각 화장실 (객체) 에 열쇠 1개
  - 들어가려면 열쇠 필요
  - 한 명만 사용 (나머지 대기)
  - 나오면 열쇠 반납

같은 객체의 여러 synchronized 메서드:
  - 같은 화장실 열쇠 공유
  - 메서드 A 사용 중이면
  - 메서드 B 도 못 들어감 (같은 열쇠)

static 락 (Class):
  - 건물 전체 마스터 열쇠
  - 모든 인스턴스 무관하게 1개
  - static 메서드는 마스터 열쇠

재진입:
  - 열쇠 가진 사람은
  - 안에서 다른 방도 (같은 열쇠로)

→ synchronized = 객체별 열쇠 1개, static 은 클래스 마스터 열쇠.


🧭 9개 섹션 로드맵

1. synchronized 메서드의 정의
2. 잠금 대상 (this)
3. 한 번에 하나의 스레드
4. static synchronized (Class 락)
5. 인스턴스 락 vs 클래스 락
6. 같은 클래스 다른 메서드 동시 호출
7. 가시성과 재진입성
8. synchronized 메서드의 단점
9. 면접 + 자기 점검

1️⃣ synchronized 메서드의 정의

1.1 synchronized 메서드

class Counter {
    private int count = 0;
    
    public synchronized void increment() {   // synchronized 키워드
        count++;   // 임계 영역 보호
    }
    
    public synchronized int getCount() {
        return count;
    }
}

1.2 동작

synchronized 메서드 동작:

  메서드 진입 시:
    - 객체의 모니터 락 획득 시도
    - 성공 → 진입 (RUNNABLE)
    - 실패 → 대기 (BLOCKED)

  메서드 종료 시:
    - 락 자동 반납

  → 한 번에 하나의 스레드만

1.3 효과

synchronized 의 효과:

상호 배제:
  - 한 번에 하나만 진입
  - 경쟁 조건 방지

가시성:
  - 변경이 다른 스레드에 보임
  - 락 해제 시 flush

원자성:
  - 메서드 전체가 원자적
  - 중간 개입 X

1.4 예시 — 안전한 카운터

class SafeCounter {
    private int count = 0;
    
    // synchronized — 안전
    public synchronized void increment() {
        count++;   // read-modify-write 가 원자적
    }
    
    public synchronized int get() {
        return count;
    }
}

// 100 스레드 × 1000 증가 = 정확히 100,000

1.5 비교 — 동기화 전후

// ❌ 동기화 X
class UnsafeCounter {
    private int count = 0;
    public void increment() {
        count++;   // 손실 가능
    }
}

// ✓ 동기화
class SafeCounter {
    private int count = 0;
    public synchronized void increment() {
        count++;   // 안전
    }
}

1.6 ILIC 의 맥락

@Service
public class ShipmentStatistics {
    
    private int totalProcessed = 0;
    private BigDecimal totalRevenue = BigDecimal.ZERO;
    
    // synchronized — 통계 안전
    public synchronized void record(Shipment shipment) {
        totalProcessed++;
        totalRevenue = totalRevenue.add(shipment.getRevenue());
        // 두 변수 일관성 보장
    }
    
    public synchronized Statistics getStatistics() {
        return new Statistics(totalProcessed, totalRevenue);
        // 일관된 스냅샷
    }
    
    record Statistics(int count, BigDecimal revenue) {}
}

1.7 자기 점검 답변

synchronized 메서드의 정의는?

:
1. 정의:

  • synchronized 키워드
  • 메서드 시그니처
  1. 동작:

    • 진입 시 락 획득
    • 종료 시 반납
    • 한 번에 하나
  2. 효과:

    • 상호 배제
    • 가시성
    • 원자성
  3. 결과:

    • 경쟁 조건 방지

2️⃣ 잠금 대상 (this)

2.1 인스턴스 메서드의 락

인스턴스 synchronized 메서드:

  잠금 대상 = this (해당 인스턴스)

  public synchronized void method() { }
  ≡
  public void method() {
      synchronized (this) {   // this 락
          ...
      }
  }

2.2 this 락의 의미

this 락:

  - 객체마다 모니터 락 1개
  - synchronized 메서드 = this 락
  - 같은 객체의 synchronized 들이 락 공유

따라서:
  - 다른 객체는 다른 락 (독립)
  - 같은 객체는 같은 락 (배타)

2.3 다른 객체는 독립

class Counter {
    private int count = 0;
    public synchronized void increment() {
        count++;
    }
}

// 객체 2개
Counter c1 = new Counter();
Counter c2 = new Counter();

// 스레드 A: c1.increment()  (c1 락)
// 스레드 B: c2.increment()  (c2 락)
// → 다른 락, 동시 실행 가능 (독립)

// 스레드 C: c1.increment()  (c1 락)
// → A 와 같은 락, 대기 (BLOCKED)

2.4 시각화

this 락:

객체 c1:               객체 c2:
  ┌─────────────┐       ┌─────────────┐
  │ 모니터 락 c1 │       │ 모니터 락 c2 │
  └─────────────┘       └─────────────┘
       ↑                      ↑
  c1.increment()         c2.increment()
  (c1 락)                (c2 락)
  
  c1 의 메서드끼리: 배타 (같은 락)
  c1 vs c2: 독립 (다른 락)

2.5 명시적 this 와 동일

// synchronized 메서드
public synchronized void method() {
    count++;
}

// 동일한 의미 (명시적 블록)
public void method() {
    synchronized (this) {
        count++;
    }
}

// 둘 다 this 락

2.6 ILIC 의 맥락

public class ShipmentProcessor {
    
    private int processedCount = 0;
    
    // this 락
    public synchronized void process(Shipment shipment) {
        // 이 인스턴스의 락
        processedCount++;
        doProcess(shipment);
    }
}

// 사용
ShipmentProcessor p1 = new ShipmentProcessor();
ShipmentProcessor p2 = new ShipmentProcessor();

// p1.process() 와 p2.process()
// → 다른 락 (독립, 동시 가능)

// p1.process() × 2 스레드
// → 같은 락 (배타, 하나씩)

// 주의: Spring 싱글톤이면 인스턴스 1개
// → 모든 요청이 같은 락 (배타)

2.7 자기 점검 답변

synchronized의 잠금 대상은?

:
1. 인스턴스 메서드:

  • this (해당 인스턴스)
  1. this 락:

    • 객체마다 락 1개
    • synchronized 들이 공유
  2. 다른 객체:

    • 다른 락 (독립)
    • 동시 가능
  3. 같은 객체:

    • 같은 락 (배타)
    • 하나씩

3️⃣ 한 번에 하나의 스레드

3.1 상호 배제

상호 배제 (Mutual Exclusion):

  한 스레드가 synchronized 메서드 진입 시
  같은 객체의 다른 스레드는 진입 불가.

원리:
  - 모니터 락 1개
  - 획득한 스레드만 진입
  - 나머지 BLOCKED

3.2 시나리오

synchronized 시나리오:

스레드 A: increment() 호출
  → c 의 락 획득
  → 진입 (RUNNABLE)
  → count++ 실행

스레드 B: increment() 호출 (동시)
  → c 의 락 없음 (A 보유)
  → BLOCKED (대기)

스레드 A: 메서드 종료
  → 락 반납

스레드 B:
  → 락 획득
  → 진입

3.3 시각화

한 번에 하나:

스레드 A: [락 획득][increment 실행][락 반납]
스레드 B:    [BLOCKED 대기────────][락 획득][실행]
                                  ↑ A 반납 후

  동시 실행 X (직렬화)
  안전하지만 병렬성 ↓

3.4 BLOCKED 상태

// synchronized 대기 = BLOCKED
class SharedCounter {
    private int count = 0;
    
    public synchronized void increment() {
        count++;
        sleep(100);   // 오래 점유
    }
}

// 스레드 A: increment() 실행 중 (락 보유)
// 스레드 B: increment() 시도 → BLOCKED
//   jstack 에서:
//   "Thread-B" BLOCKED (on object monitor)
//     waiting to lock <0x...> 

3.5 직렬화의 영향

synchronized 의 직렬화:

  여러 스레드가 같은 객체 메서드:
    - 한 번에 하나
    - 사실상 순차 실행
    - 병렬성 상실

  성능 영향:
    - 임계 영역 길수록 ↓
    - 경합 심할수록 ↓

→ 범위 최소화 (다음 Unit)

3.6 ILIC 의 맥락

@Service
public class ShipmentSequential {
    
    private int counter = 0;
    
    // synchronized — 직렬화
    public synchronized void process(Shipment shipment) {
        counter++;
        
        // ❌ 무거운 작업도 락 안에 (나쁨)
        callExternalApi(shipment);   // 다른 스레드 모두 대기
        repository.save(shipment);   // 직렬화
        
        // 모든 스레드가 하나씩 대기
        // 병렬성 X
    }
    
    // 개선 — 범위 최소화 (Unit 4.3 에서)
    public void processBetter(Shipment shipment) {
        // 무거운 작업은 밖에서 (병렬)
        callExternalApi(shipment);
        repository.save(shipment);
        
        // 임계 영역만 동기화
        synchronized (this) {
            counter++;   // 짧게
        }
    }
    
    private void callExternalApi(Shipment s) { }
}

3.7 자기 점검 답변

한 번에 하나의 스레드 원리는?

:
1. 상호 배제:

  • 모니터 락 1개
  • 획득한 스레드만
  1. 시나리오:

    • A 락 획득 (진입)
    • B 대기 (BLOCKED)
    • A 반납 → B 진입
  2. 상태:

    • 대기 = BLOCKED
  3. 영향:

    • 직렬화
    • 병렬성 ↓
    • 범위 최소화 필요

4️⃣ static synchronized (Class 락)

4.1 static synchronized

class Counter {
    private static int staticCount = 0;
    
    public static synchronized void incrementStatic() {
        staticCount++;
    }
}

4.2 잠금 대상 = Class 객체

static synchronized 잠금 대상:

  = 그 클래스의 Class 객체
  (Counter.class)

  public static synchronized void method() { }
  ≡
  public static void method() {
      synchronized (Counter.class) {
          ...
      }
  }

4.3 Class 락의 의미

Class 락:

  - 클래스당 Class 객체 1개
  - 모든 인스턴스 무관
  - static synchronized 들이 공유

따라서:
  - 인스턴스가 몇 개든 Class 락 1개
  - 모든 static synchronized 가 배타

4.4 인스턴스 무관

class Counter {
    private static int count = 0;
    
    public static synchronized void increment() {
        count++;   // Class 락
    }
}

// 인스턴스 무관
Counter c1 = new Counter();
Counter c2 = new Counter();

// 스레드 A: Counter.increment() (또는 c1.increment())
// 스레드 B: Counter.increment() (또는 c2.increment())
// → 같은 Class 락 (배타)
// → 인스턴스 달라도 직렬화

4.5 시각화

Class 락:

       Counter.class (Class 객체)
       ┌──────────────┐
       │ Class 모니터  │  ← 클래스당 1개
       └──────────────┘
            ↑
   ┌────────┼────────┐
   │        │        │
 c1.static c2.static Counter.static
   (모두 같은 Class 락)
   
   인스턴스 무관, 모두 배타

4.6 ILIC 의 맥락

public class ShipmentIdGenerator {
    
    private static long lastId = 0;
    
    // static synchronized — Class 락
    public static synchronized long nextId() {
        return ++lastId;   // 모든 인스턴스/스레드 공유
    }
    // 클래스 레벨 동기화
    // 어떤 인스턴스에서 호출해도 같은 락
}

// 사용
long id1 = ShipmentIdGenerator.nextId();
long id2 = ShipmentIdGenerator.nextId();
// 고유 ID 보장 (Class 락)

// 실무: AtomicLong 권장
public class BetterIdGenerator {
    private static final AtomicLong counter = new AtomicLong();
    public static long nextId() {
        return counter.incrementAndGet();   // 락 없이 원자적
    }
}

4.7 자기 점검 답변

static synchronized의 잠금 대상은?

:
1. 잠금 대상:

  • Class 객체 (Counter.class)
  1. Class 락:

    • 클래스당 1개
    • 인스턴스 무관
  2. 배타:

    • 모든 static synchronized
    • 인스턴스 달라도 직렬화
  3. 동등:

    • synchronized (Counter.class)

5️⃣ 인스턴스 락 vs 클래스 락

5.1 두 락의 비교

항목인스턴스 락 (this)클래스 락 (Class)
대상인스턴스 (this)Class 객체
메서드synchronizedstatic synchronized
범위객체별클래스 전체
인스턴스객체마다 락무관 (1개)

5.2 독립적인 두 락

인스턴스 락 ↔ 클래스 락:

  서로 다른 락!

  인스턴스 synchronized: this 락
  static synchronized: Class 락

  → 동시 실행 가능 (다른 락)

5.3 동시 실행 예시

class Counter {
    private int instanceCount = 0;
    private static int staticCount = 0;
    
    public synchronized void incInstance() {   // this 락
        instanceCount++;
    }
    
    public static synchronized void incStatic() {   // Class 락
        staticCount++;
    }
}

Counter c = new Counter();

// 스레드 A: c.incInstance()  (this 락)
// 스레드 B: Counter.incStatic()  (Class 락)
// → 다른 락, 동시 실행 가능!

5.4 시각화

인스턴스 락 vs 클래스 락:

객체 c:                    Counter.class:
  ┌──────────┐              ┌──────────┐
  │ this 락   │              │ Class 락  │
  └──────────┘              └──────────┘
       ↑                         ↑
  incInstance()             incStatic()
  (this 락)                 (Class 락)
  
  서로 다른 락 → 동시 가능

5.5 혼동 주의

// ❌ 혼동 — 인스턴스 + static 같은 변수?
class Confused {
    private static int count = 0;
    
    // 인스턴스 락으로 static 보호 (위험!)
    public synchronized void increment() {
        count++;   // static 인데 this 락
    }
    // 여러 인스턴스가 각자 this 락
    // → static count 보호 안 됨!
}

// ✓ static 은 static synchronized (또는 Class 락)
class Correct {
    private static int count = 0;
    
    public static synchronized void increment() {
        count++;   // Class 락으로 보호
    }
}

5.6 ILIC 의 맥락

public class LockComparison {
    
    private int instanceData = 0;
    private static int sharedData = 0;
    
    // 인스턴스 데이터 — this 락
    public synchronized void updateInstance() {
        instanceData++;   // this 락 (객체별)
    }
    
    // static 데이터 — Class 락
    public static synchronized void updateShared() {
        sharedData++;   // Class 락 (전역)
    }
    
    // 주의: 둘은 독립
    // updateInstance() 와 updateShared() 동시 가능
    
    // ❌ 잘못 — static 을 this 락으로
    public synchronized void wrongUpdate() {
        sharedData++;   // ★ this 락은 static 보호 못 함
        // 여러 인스턴스 → 각자 락 → 경쟁
    }
}

5.7 자기 점검 답변

인스턴스 락 vs 클래스 락은?

:
1. 인스턴스 락:

  • this
  • synchronized 메서드
  • 객체별
  1. 클래스 락:

    • Class 객체
    • static synchronized
    • 클래스 전체
  2. 독립:

    • 서로 다른 락
    • 동시 가능
  3. 주의:

    • static 은 Class 락으로
    • this 락은 static 보호 X

6️⃣ 같은 클래스 다른 메서드 동시 호출

6.1 핵심 질문

질문:
  같은 클래스의 다른 synchronized 메서드
  둘이 동시 호출 가능한가?

답:
  같은 객체면 불가 (같은 락 공유).
  다른 객체면 가능.

6.2 같은 객체 — 불가

class Account {
    private int balance = 1000;
    
    public synchronized void deposit(int amount) {   // this 락
        balance += amount;
    }
    
    public synchronized void withdraw(int amount) {   // this 락 (같음!)
        balance -= amount;
    }
}

Account account = new Account();

// 스레드 A: account.deposit(100)  (this 락)
// 스레드 B: account.withdraw(50)  (this 락, 같음!)
// → 같은 락, 동시 불가
// → B 는 BLOCKED (A 끝날 때까지)

6.3 왜 불가능한가

같은 객체의 synchronized 메서드들:

  모두 this 락 사용.
  → 락 1개 공유.

  deposit() 진입 시 this 락 획득.
  withdraw() 도 this 락 필요.
  → 이미 deposit 이 보유.
  → withdraw 대기 (BLOCKED).

핵심:
  - 메서드가 달라도 같은 락
  - 하나만 진입

6.4 시각화

같은 객체의 다른 메서드:

account 의 this 락 (1개):
  
스레드 A: deposit() ─── this 락 획득
스레드 B: withdraw() ── this 락 대기 (BLOCKED)
                          ↑ A 가 보유 중

  메서드 다르지만 같은 락
  → 동시 불가

6.5 다른 객체 — 가능

Account a1 = new Account();
Account a2 = new Account();

// 스레드 A: a1.deposit(100)   (a1 의 this 락)
// 스레드 B: a2.withdraw(50)   (a2 의 this 락, 다름!)
// → 다른 락, 동시 가능

6.6 비동기화 메서드는 영향 없음

class Mixed {
    private int count = 0;
    
    public synchronized void syncMethod() {   // this 락
        count++;
    }
    
    public void normalMethod() {   // 락 없음
        // 동기화 X
        readSomething();
    }
}

Mixed m = new Mixed();

// 스레드 A: m.syncMethod()  (this 락)
// 스레드 B: m.normalMethod()  (락 X)
// → 동시 가능 (normalMethod 는 락 안 씀)
// 단, normalMethod 가 count 접근 시 위험

6.7 ILIC 의 맥락

@Service
public class ShipmentAccount {
    
    private BigDecimal balance = BigDecimal.ZERO;
    
    // 모두 this 락 (같은 객체면 배타)
    public synchronized void charge(BigDecimal amount) {
        balance = balance.add(amount);
    }
    
    public synchronized void refund(BigDecimal amount) {
        balance = balance.subtract(amount);
    }
    
    public synchronized BigDecimal getBalance() {
        return balance;
    }
    // charge, refund, getBalance 모두 같은 객체 this 락
    // → 동시 호출 불가 (하나씩)
    // → balance 일관성 보장
    
    // Spring 싱글톤이면 모든 요청이 같은 인스턴스
    // → 모든 메서드 직렬화 (안전하지만 병목 가능)
}

6.8 자기 점검 답변

같은 클래스의 다른 synchronized 메서드 둘이 동시 호출 가능한가?

:
1. 같은 객체:

  • 불가
  • 같은 this 락 공유
  1. 이유:

    • 메서드 달라도 this 락
    • 하나만 진입
  2. 다른 객체:

    • 가능
    • 다른 락
  3. 비동기화 메서드:

    • 영향 없음
    • 동시 가능 (락 X)

7️⃣ 가시성과 재진입성

7.1 synchronized 의 가시성

synchronized 의 가시성 보장:

  락 획득 시:
    - 메인 메모리에서 최신 값 읽기

  락 해제 시:
    - 변경을 메인 메모리에 flush

  → 다른 스레드가 변경 봄
  → happens-before 보장

7.2 가시성 예시

class VisibilityExample {
    private boolean flag = false;
    private int data = 0;
    
    public synchronized void write() {
        data = 42;
        flag = true;
        // 락 해제 시 flush
    }
    
    public synchronized boolean read() {
        // 락 획득 시 최신 읽기
        if (flag) {
            return data == 42;   // true 보장
        }
        return false;
    }
    // synchronized 가 가시성 보장
}

7.3 재진입성 (Reentrant)

재진입성 (Reentrant):

  같은 스레드는 이미 가진 락을
  다시 획득 가능.

  → 데드락 방지
  → 중첩 synchronized OK

7.4 재진입 예시

class Reentrant {
    
    public synchronized void outer() {
        inner();   // 같은 객체의 다른 synchronized 호출
        // 이미 this 락 보유 → inner 진입 OK
    }
    
    public synchronized void inner() {
        // outer 가 이미 this 락 보유
        // 같은 스레드라 재진입 가능
    }
}

// outer() 호출:
// 1. this 락 획득
// 2. inner() 호출
// 3. this 락 이미 보유 → 재진입 (OK)
// 재진입 없으면 데드락 (자기 자신 대기)

7.5 재진입 카운트

재진입 메커니즘:

  락에 카운트 + 소유 스레드.

  획득: 카운트++
  반납: 카운트--

  같은 스레드 재획득: 카운트++
  카운트 0 시 완전 반납

예:
  outer 진입: 카운트 1
  inner 진입: 카운트 2
  inner 종료: 카운트 1
  outer 종료: 카운트 0 (반납)

7.6 ILIC 의 맥락

@Service
public class ReentrantExample {
    
    private int count = 0;
    
    // 재진입 — 중첩 synchronized
    public synchronized void processAll(List<Shipment> shipments) {
        // this 락 획득
        for (Shipment s : shipments) {
            processOne(s);   // 같은 객체 synchronized 호출
            // 이미 this 락 보유 → 재진입 OK
        }
    }
    
    public synchronized void processOne(Shipment shipment) {
        // processAll 에서 호출 시 재진입
        count++;
        doProcess(shipment);
    }
    // 재진입 없으면 데드락 (자기 락 대기)
    // synchronized 는 재진입 가능 → 안전
    
    private void doProcess(Shipment s) { }
}

7.7 자기 점검 답변

가시성과 재진입성은?

:
1. 가시성:

  • 락 획득: 최신 읽기
  • 락 해제: flush
  • happens-before
  1. 재진입성:

    • 같은 스레드 재획득
    • 데드락 방지
    • 중첩 OK
  2. 재진입 메커니즘:

    • 카운트 + 소유 스레드
    • 0 시 완전 반납
  3. 효과:

    • 중첩 synchronized 안전

8️⃣ synchronized 메서드의 단점

8.1 단점들

synchronized 메서드의 단점:

1. 범위가 넓음
   - 메서드 전체 잠금
   - 불필요한 부분도 직렬화

2. 잠금 대상 고정
   - this 또는 Class
   - 세밀한 제어 X

3. 성능
   - 직렬화 (병렬성 ↓)
   - 락 오버헤드

4. 유연성 부족
   - 타임아웃 X
   - 인터럽트 X
   - tryLock X (Phase 5)

8.2 범위가 넓은 문제

// ❌ 메서드 전체 잠금 (넓음)
public synchronized void process(Shipment shipment) {
    // 무거운 작업 (락 불필요)
    BigDecimal freight = expensiveCalculation(shipment);
    Tracking tracking = callExternalApi(shipment);
    
    // 실제 임계 영역 (락 필요)
    count++;
    
    // 더 무거운 작업 (락 불필요)
    repository.save(shipment);
    
    // 전체가 동기화 → 다른 스레드 모두 대기
}

// ✓ 블록으로 범위 최소화 (Unit 4.3)
public void processBetter(Shipment shipment) {
    BigDecimal freight = expensiveCalculation(shipment);   // 병렬
    Tracking tracking = callExternalApi(shipment);          // 병렬
    
    synchronized (this) {
        count++;   // 임계 영역만
    }
    
    repository.save(shipment);   // 병렬
}

8.3 잠금 대상 고정

// synchronized 메서드 — this 또는 Class 고정
public synchronized void method() {
    // this 락만
}

// 다른 락 객체 쓰고 싶으면?
// → synchronized 블록 (Unit 4.3)
private final Object lock = new Object();
public void method2() {
    synchronized (lock) {   // 별도 락
        // ...
    }
}

8.4 유연성 부족

synchronized 의 유연성 부족:

  - 타임아웃 불가 (무한 대기)
  - 인터럽트 불가 (대기 중 깰 수 없음)
  - tryLock 불가 (시도 후 포기 X)
  - 공정성 보장 X

해결:
  - ReentrantLock (Phase 5)
  - lock(), tryLock(), lockInterruptibly()

8.5 단점 정리

synchronized vs Lock (Phase 5 예고):

synchronized:
  + 간단 (키워드)
  + 자동 반납
  + 재진입
  - 범위 (메서드 전체)
  - 유연성 부족

ReentrantLock:
  + 유연 (타임아웃, 인터럽트, tryLock)
  + 공정성 옵션
  - try-finally 필수
  - 복잡

8.6 ILIC 의 맥락

@Service
public class SynchronizedDisadvantage {
    
    private int counter = 0;
    
    // ❌ 단점 — 넓은 범위
    public synchronized void processWide(Shipment shipment) {
        // 무거운 작업도 락 안에
        callExternalApi(shipment);   // 5초 (모든 스레드 대기)
        counter++;
        repository.save(shipment);   // 1초 (직렬화)
    }
    
    // ✓ 개선 — 범위 최소화
    public void processNarrow(Shipment shipment) {
        callExternalApi(shipment);   // 병렬
        
        synchronized (this) {
            counter++;   // 짧은 임계 영역만
        }
        
        repository.save(shipment);   // 병렬
    }
    
    // ✓✓ Atomic (락 없이)
    private final AtomicInteger atomicCounter = new AtomicInteger();
    public void processAtomic(Shipment shipment) {
        callExternalApi(shipment);
        atomicCounter.incrementAndGet();   // 락 없이 원자적
        repository.save(shipment);
    }
    
    private void callExternalApi(Shipment s) { }
}

8.7 자기 점검 답변

synchronized 메서드의 단점은?

:
1. 넓은 범위:

  • 메서드 전체
  • 불필요한 부분 직렬화
  1. 잠금 대상 고정:

    • this 또는 Class
    • 세밀한 제어 X
  2. 성능:

    • 직렬화
    • 병렬성 ↓
  3. 유연성 부족:

    • 타임아웃, 인터럽트 X
    • → Lock (Phase 5)

9️⃣ 면접 + 자기 점검

9.1 면접 단골 질문 매핑

Q핵심 답변
synchronized 메서드?키워드, 한 번에 하나
인스턴스 락?this
static 락?Class 객체
같은 객체 다른 메서드?불가 (같은 락)
다른 객체?가능 (다른 락)
인스턴스 vs Class 락?독립 (동시 가능)
가시성 보장?락 해제 시 flush
재진입성?같은 스레드 재획득
대기 상태?BLOCKED
단점?범위 넓음, 유연성 부족

9.2 자기 점검 체크리스트

synchronized 메서드

  • 정의
  • 동작
  • 효과

잠금 대상

  • 인스턴스: this
  • static: Class

한 번에 하나

  • 상호 배제
  • BLOCKED

락 비교

  • 인스턴스 vs Class
  • 독립

같은 클래스 메서드

  • 같은 객체 불가
  • 다른 객체 가능

가시성/재진입

  • 가시성 보장
  • 재진입성

단점

  • 범위
  • 유연성

9.3 추가 심화 질문

Q1: synchronized 와 happens-before?

답:

  • 락 해제 happens-before 다음 락 획득
  • 변경이 다음 획득 스레드에 보임
  • JMM (Java Memory Model)
  • 가시성 + 순서 보장

Q2: 생성자에 synchronized?

답:

  • 불가 (컴파일 에러)
  • 생성자는 한 스레드만 (객체 생성 중)
  • 동기화 불필요
  • 단, this escape 주의

Q3: synchronized 성능 (Java 6+)?

답:

  • Biased Locking (편향 락)
  • Lightweight Locking (경량 락)
  • 경합 없으면 빠름
  • 경합 시 무거운 락 (모니터)

Q4: 같은 락 객체 다른 클래스?

답:

  • 락은 객체 단위
  • 같은 객체 락이면 클래스 무관
  • synchronized(sharedLock) 공유 가능

Q5: synchronized 와 wait/notify?

답:

  • wait/notify 는 synchronized 안에서만
  • 모니터 락 필요
  • 락 보유 상태에서 호출
  • Phase 6 에서 정밀

🎯 핵심 요약 — 3줄 정리

1. synchronized 메서드

  • 키워드, 한 번에 하나의 스레드
  • 인스턴스: this 락, static: Class 락

2. 락 공유

  • 같은 객체의 synchronized 들: 같은 락 (배타)
  • 다른 객체, 인스턴스 vs Class: 독립 (동시 가능)

3. 특성과 단점

  • 가시성 + 재진입성 보장
  • 단점: 범위 넓음, 유연성 부족 (→ 블록, Lock)

📚 다음으로...

Unit 4.3 — synchronized 블록

이번 Unit에서 synchronized 메서드를 봤다면, 다음은 synchronized 블록 (범위 최소화).

  • 블록으로 범위 최소화
  • 잠금 대상 명시
  • 메서드 vs 블록 선택

Phase 4 진행 상황

🚀 Phase 4 — synchronized & volatile (★ 1차 정점)
  ✅ Unit 4.1 임계 영역과 동기화의 필요성
  ✅ Unit 4.2 synchronized 메서드 ← 여기
  ⏭ Unit 4.3 synchronized 블록
  ⏭ Unit 4.4 모니터 락 (★ 마스터)
  ⏭ Unit 4.5 volatile (★ 마스터)

4주차 누적 진행

✅ Phase 1 — 동시성의 기초 (4 Unit)
✅ Phase 2 — 4분면 매트릭스 (3 Unit)
✅ Phase 3 — 스레드 다루기 (5 Unit)
🚀 Phase 4 — synchronized & volatile (2/5 진행) ★ 1차 정점

총: 14/35 Unit
profile
Software Developer

0개의 댓글