목차
0. 들어가며
자바로 멀티스레드 프로그래밍을 하다 보면 필연적으로 마주치는 키워드가 바로 synchronized
다. 최근까지 나는 synchronized
키워드의 원리를 정확히 이해하지 못한 채 단순히 "동시 접근을 막는다" 정도로만 알고 사용했다. 이번에 개념을 명확히 정리하면서 특히 "모니터 락(Monitor Lock)"이라는 개념을 접하게 되어 많은 것을 새롭게 깨달았다.
1. 모니터 락이란?
모니터 락은 자바의 모든 객체에 내장된 고유한 락이다. 자바는 모든 객체가 생성될 때 내부적으로 하나의 락을 기본으로 제공한다. 이를 "내재적 락(intrinsic lock)"이라 하며, synchronized 키워드가 사용될 때 이 락을 이용해서 동기화를 처리한다.
쉽게 말하면, synchronized를 붙인 메서드나 코드 블록을 실행하려면 해당 객체의 모니터 락을 획득해야 하고, 다른 스레드는 이 락이 풀릴 때까지 접근하지 못한다. 이로써 데이터의 동시 접근으로 인한 문제를 방지할 수 있다.
2. synchronized 사용법과 모니터 락
synchronized 메서드와 synchronized 블록의 차이
1. synchronized 메서드
public synchronized void method() {
// 임계 구역 코드
}
- 암묵적으로 현재 객체(this)의 모니터 락을 사용한다.
- 메서드 전체가 하나의 임계 구역이다.
2. synchronized 블록
synchronized(객체) {
// 임계 구역 코드
}
- 괄호 안에 명시한 객체의 모니터 락을 사용한다.
- 특정 코드 영역만 임계 구역으로 설정할 수 있어 보다 유연한 동기화를 구현할 수 있다.
wait(), notify()와 모니터 락의 관계
자바의 객체는 모니터 락과 함께 대기(wait set)라는 개념을 내재하고 있다. 스레드가 wait()을 호출하면, 해당 객체의 모니터 락을 놓고 대기 집합에 들어가 멈춰 있게 된다. 이후 다른 스레드가 notify()를 호출하면, 대기 집합에 있는 스레드 중 하나를 깨우고, 깨어난 스레드는 다시 락을 획득할 수 있는 기회를 얻는다.
synchronized(queue) {
while(queue.isEmpty()) {
queue.wait(); // 큐가 비어 있으면 기다린다.
}
int value = queue.poll();
queue.notify(); // 다른 대기 중인 스레드를 깨운다.
}
3. 모니터 락의 내부 동작과 성능 최적화
모든 객체에 모니터 락이 내재된 이유
처음에는 "모든 객체가 모니터 락을 가진다면 리소스 낭비가 심하지 않을까?"라는 의문이 들었다. 하지만 실제로 자바는 객체 생성 시 객체 헤더에 모니터 락을 관리할 공간을 미리 할당하여 큰 리소스를 소모하지 않는다. 또한 JVM 내부에서 다양한 최적화 기술을 통해 성능 문제도 최소화하고 있다.
모든 객체가 내재적 락을 가짐으로써 다음과 같은 큰 장점이 있다.
- 간편하고 일관된 동기화 메커니즘 제공
- 별도의 락 객체를 생성하고 관리할 필요 없음
- 낮은 오버헤드와 JVM의 최적화 덕분에 높은 성능 유지 가능
모니터 락의 최적화 기법
자바 JVM은 모니터 락을 효율적으로 관리하기 위해 바이어스 락(biased lock), 경량 락(lightweight lock) 등의 최적화 기술을 사용한다. 이러한 기술 덕분에 멀티스레드 환경에서도 성능을 효율적으로 유지할 수 있다.
4. 모니터 락 사용 시 주의할 점
교착 상태(Deadlock)와 모니터 락
모니터 락을 사용할 때 주의할 점 중 하나는 교착 상태(deadlock)의 가능성이다.
교착 상태란 두 개 이상의 스레드가 서로 상대가 가진 락을 기다리면서 무한히 멈춰 있는 상태를 말한다. 이를 예방하기 위해 락의 획득 순서를 일관되게 유지하거나, 가능한 락 사용을 최소화해야 한다.
모니터 락 사용 시 주의사항
모니터 락을 잘못 사용하면 성능 문제나 로직 오류를 초래할 수 있다. 예를 들어, synchronized 블록 내에서 오랜 시간이 걸리는 작업을 수행하거나, 필요 이상의 넓은 범위에 동기화를 설정하면 애플리케이션의 성능 저하를 유발할 수 있다.
5. 마치며
내가 기존에 가지고 있던 오개념 중 하나는 서로 다른 synchronized 메서드는 각자 별도의 락을 사용할 거라는 생각이었다. 하지만 실제로 같은 객체에 속한 synchronized 메서드는 모두 같은 모니터 락을 사용한다. 따라서 한 메서드가 실행 중이면 다른 synchronized 메서드는 락이 풀릴 때까지 기다리게 된다.
모니터 락은 자바 동기화의 핵심이며, synchronized, wait(), notify() 같은 동기화 관련 메서드들의 기반이다. 모든 객체가 기본으로 내재된 락을 가지는 설계는 매우 효율적이고 강력한 동기화 수단을 개발자에게 제공한다는 점을 깨달았다.
자바의 모니터 락을 제대로 이해한 덕분에 앞으로의 멀티스레드 프로그래밍을 더 자신 있게 다룰 수 있을 것 같다.
'Java > Java 유용한 클래스' 카테고리의 다른 글
Java ExecutorService (0) | 2025.04.18 |
---|---|
Java ThreadLocal에 대해서 (0) | 2025.04.17 |
Java에서 BufferedInputStream과 BufferedOutputStream의 동작 원리 정리 (0) | 2025.04.08 |
Optional <T> (0) | 2024.10.02 |
Java 유용한 클래스 (0) | 2024.10.01 |