개요
우리가 보통 동기화 환경을 구축할 때, 아래의 예제 코드와 같이 동기화 환경이 설정된 메서드에 진입하기 위해선 락을 획득해야 한다고 한다.
public OrderService {
...
public synchronized void createOrder(OrderCreateRequest request) {
// 주문 생성 & 재고 차감 로직
}
...
}
그렇다면 이 락이 뭐고, 도대체 어디에 있는지에 대해 알아보자
락
락(Lock)은 동기화를 구현하기 위한 기술로, 여러 스레드(사용자)가 공유 리소스에 접근할 때 데이터의 무결성을 보장하기 위해 사용된다. 락은 동시에 하나의 스레드만 특정 리소스에 접근할 수 있도록 제어하는 역할을 한다.
쉽게 설명하면 데이터 무결성 보장? 이건 데이터가 잘못된 상태가 되지 못하도록 보장한다는 것이다.
내 계좌엔 100,000원이 있고, 10,000원짜리 제품을 하나 구매했다면 내 계좌엔 총 90,000원이 남아 있어야 할 것이다. 하지만 내부 로직의 문제로 인해 내 계좌에 50,000원만 남는다면 이건 데이터의 무결성이 지켜지지 않은 것이다.
그리고 이러한 문제가 발생한 원인은 다른 스레드와 동시에 접근하여 발생한 동시성 문제일 가능성이 크다.
그렇다면 여기서 사용되는 락은 어디에 있을까?
바로 JVM 힙 메모리 영역의 객체 헤더에 있다.
만약 스레드가 OrderService의 orderedProduct() 메서드를 실행하려고 하기 전, 힙 영역에 저장된 OrderService의 헤더에 락이 있는지 확인하고, 있다면 해당 락을 획득해 작업을 이어 진행하는 것이다.
이러한 과정을 모니터 락 메커니즘 이라고 한다.
참고로 락 획득 과정은 객체 헤더의 Mark Word에는 락의 상태와 소유 스레드 정보가 기록되는데, 여기에 락을 점유 중인 스레드 정보가 없다면 락을 획득할 수 있는 것이다.
Mark Word.. 난생 처음 들어봤네.. 미안하오..
그렇다면 만약 아래와 같은 OrderService가 있다고 하자.
public OrderService {
...
public synchronized void createOrder(OrderCreateRequest request) {
// 주문 생성 & 재고 차감 로직
}
public synchronized void cancelOrder(OrderCancelRequest request) {
// 주문 취소 & 재고 복구 로직
}
...
}
createOrder(), cancelOrder() 메서드 둘 다 synchronized 키워드가 달려있다. 둘 다 동기화 환경을 제공해야 하기 때문에 메서드 실행 전 락 획득을 시도할 것이다.
만약 A 스레드가 createOrder() 요청, B 스레드가 cancelOrder() 요청을 보냈을 경우, 두 요청은 엄연히 다른 작업을 수행하는 스레드이다.
하지만 OrderService 내부의 메서드이기 때문에 서로 같은 락 객체를 사용하나??
그렇다.
대표적인 동기화 기술은 synchronized 키워드는 인스턴스 수준의 락을 사용하기 때문이다.
그렇기 때문에 락 객체를 더 유연하게 사용하기 위해선 ReentrantLock을 사용하는 것이 좋다.
느낀 점
요즘 더 나은 성능을 가진 코드를 만들려고 하니까, 자연스럽게 요청을 동시에 처리하는 병렬성에 대해 고민할 수밖에 없다.
그러면 또 동시성 문제와 같은 고민이 따라온다.
골치가 아프다.. 멀티스레드 강의를 들었을 땐 확실하게 이해했었는데.. 그래도 그 강의를 들어서 멀티스레드와 같은 기법을 이해한 것 같기도 하고…
'개발 일기' 카테고리의 다른 글
[개발 일기] 2025.01.07 - OSI 7계층 (데이터 링크 계층) (0) | 2025.01.07 |
---|---|
[개발 일기] 2025.01.06 - OSI 7계층 (물리 계층) (0) | 2025.01.06 |
[개발 일기] 2025.01.04 - Event, Publisher, Listener (0) | 2025.01.04 |
[개발 일기] 2025.01.03 - Redis의 만료기한 (0) | 2025.01.03 |
[개발 일기] 2025.01.02 - @Transactional (readOnly = true) (0) | 2025.01.02 |