뮤텍스 락
공유 자원에 동시에 하나의 스레드만 접근할 수 있도록 보장하는 상호 배제(Mutual Exclusion) 동기화 메커니즘입니다.
핵심 원리
뮤텍스 = 자물쇠
스레드 1: 락 획득 → 작업 중 → 락 해제
스레드 2: 락 획득 시도 → 대기 → 락 해제 후 획득 → 작업
스레드 3: 락 획득 시도 → 대기 → ...
→ 공유 자원에 항상 1개 스레드만 접근
뮤텍스가 없을 때 문제 (Race Condition)
// 잔액 출금 - 동시 접근 시 문제 발생
int balance = 1000;
// 스레드 1, 2가 동시에 실행되면?
// 스레드 1: balance 읽기(1000) → 계산(1000-500=500)
// 스레드 2: balance 읽기(1000) → 계산(1000-500=500)
// 스레드 1: balance = 500 저장
// 스레드 2: balance = 500 저장 ← 500이 두 번 출금됐는데 잔액이 500!
balance -= 500;
Java에서 뮤텍스 구현
// 1. synchronized 키워드
class BankAccount {
private int balance = 1000;
synchronized void withdraw(int amount) { // 메서드 단위 락
if (balance >= amount)
balance -= amount;
}
}
// 2. ReentrantLock (명시적 락)
class BankAccount {
private int balance = 1000;
private final Lock lock = new ReentrantLock();
void withdraw(int amount) {
lock.lock(); // 락 획득
try {
if (balance >= amount)
balance -= amount;
} finally {
lock.unlock(); // 반드시 해제 (finally 필수!)
}
}
}
synchronized vs ReentrantLock
| 항목 | synchronized | ReentrantLock |
|---|---|---|
| 락 획득 실패 시 | 무한 대기 | tryLock()으로 타임아웃 가능 |
| 락 해제 | 자동 | 수동 (finally 필수) |
| 공정성 설정 | 불가 | 가능 (new ReentrantLock(true)) |
| 조건 변수 | wait/notify | Condition 객체 |
| 코드 복잡도 | 단순 O | 복잡 |
뮤텍스 vs 세마포어
| 항목 | 뮤텍스 | 세마포어 |
|---|---|---|
| 접근 허용 수 | 1개 스레드 | N개 스레드 |
| 소유권 | 락 획득한 스레드만 해제 | 누구나 해제 가능 |
| 용도 | 단일 자원 보호 | 자원 개수 제한 |
| 예시 | DB 커넥션 1개 | DB 커넥션 풀 10개 |
주의사항: 데드락
스레드 1: A 락 획득 → B 락 대기
스레드 2: B 락 획득 → A 락 대기
→ 서로 무한 대기 = 데드락 발생!
// 해결: 항상 동일한 순서로 락 획득
void transfer(Account from, Account to) {
Account first = from.id < to.id ? from : to; // 항상 id 작은 것 먼저
Account second = from.id < to.id ? to : from;
synchronized (first) {
synchronized (second) {
// 이체 로직
}
}
}
뮤텍스는 강력하지만 잘못 사용하면 데드락, 성능 저하를 유발합니다. 락의 범위는 최대한 좁게 유지하는 것이 중요합니다.