이전 글에서 우리는 세마포어를 사용하여 동시성 문제를 해결하는 방법을 살펴보았습니다.
그러나 동시성 제어에 대한 탐구는 여기서 그치지 않고, 이번 글에서는 뮤텍스(Mutex)를 활용하여 동시성 문제를 해결해보려고 합니다.
뮤텍스(Mutex)는 여러 스레드가 동시에 공유 자원에 접근하는 것을 막아주는 동기화 메커니즘입니다.
뮤텍스는 한 번에 하나의 스레드만 공유 자원에 접근할 수 있게 하여 데이터의 일관성을 유지할 수 있도록 도와줍니다.
private final Lock lock = new ReentrantLock();
@PostMapping("/bookApplyDonation")
public ResponseEntity<MessageDto> createBookApplyDonation(@RequestBody BookApplyDonationRequestDto bookApplyDonationRequestDto) {
try {
lock.lock();
return ResponseEntity.ok().body(bookApplyDonationService.createBookApplyDonation(bookApplyDonationRequestDto));
} finally {
lock.unlock();
}
}
ReentrantLock
을 사용하여 뮤텍스를 구현하였습니다.
lock()
메소드를 호출함으로써 자원에 대한 접근을 시도하고 unlock()
메소드를 호출함으로써 자원의 접근 권한을 해제합니다.
이번 성능 테스트의 대상은 33번 책이며 성능 테스트 도구로는 JMeter를 사용합니다. JMeter 설정은 이전 테스트와 동일합니다.
성능 테스트 결과를 살펴보면 뮤텍스를 사용했을 때 세마포어를 사용했을 때보다 더 좋은 성능을 보이는 것을 알 수 있습니다.
세마포어를 사용했을 때는 응답 시간이 0.5 ~ 0.7초 사이에 분포하는 반면, 뮤텍스를 사용했을 때는 0.5 ~ 0.6초 사이에 분포합니다. 이를 통해 뮤텍스가 세마포어보다 더 효율적으로 동시성 문제를 해결할 수 있음을 확인할 수 있습니다.
단 하나의 스레드만이 공유 자원에 접근 가능하게 하는 상황에서 뮤텍스가 세마포어보다 더 좋은 성능을 보이는 이유는 아래와 같습니다.
단순성
뮤텍스는 사용 방법이 상대적으로 간단하며, 개발자가 이해하기 쉽습니다.
뮤텍스는 공유 자원에 대한 접근 권한을 얻기 위해 lock을 획득하고 작업을 마친 후에 unlock을 통해 권한을 해제합니다.
오버헤드 감소
뮤텍스는 공유 자원에 대한 접근 권한을 요청할 때 필요한 시스템 자원과 처리 시간의 오버헤드가 적습니다.
반면에, 세마포어는 내부적으로 여러 상태와 조건을 검사해야 하므로 더 많은 오버헤드가 발생할 수 있습니다.
데드락 방지
뮤텍스는 재진입 가능한 구조로 설계되어 있어, lock을 이미 획득한 스레드가 다시 lock을 요청하더라도 데드락에 빠지지 않습니다.
세마포어는 이러한 상황을 직접 관리해야 하므로 복잡성이 증가하고, 데드락 발생 가능성이 높아집니다.
우선순위 역전 문제 해결
뮤텍스는 우선순위 역전 문제를 해결하기 위한 메커니즘을 제공합니다.
이는 낮은 우선순위의 스레드가 자원을 점유하고 있어서 높은 우선순위의 스레드가 블록되는 문제를 방지해줍니다.
즉 뮤텍스는 단 하나의 스레드만이 공유 자원에 접근 가능하게 하는 상황에서 세마포어보다 효율적입니다.