JunhoCho's Dev Log home dev life interests

[Android/Kotlin] Coroutines Mutex Deep Dive: 동시성 제어 정복하기

26 Jan 2026

안드로이드 개발을 하다 보면 여러 개의 코루틴이 하나의 변수나 자원을 동시에 수정하려고 할 때 데이터가 꼬이는 현상을 마주하게 됩니다. 오늘은 이를 해결하기 위한 핵심 도구인 Mutex와 그 대안들에 대해 깊게 파헤쳐 보겠습니다.


1. 문제의 시작: Race Condition (경쟁 상태)

여러 개의 코루틴이 동시다발적으로 공유 자원을 수정하면, 연산 결과가 유실되는 현상이 발생합니다.

var counter = 0
val jobs = List(100) {
    launch(Dispatchers.Default) {
        repeat(1000) { counter++ }
    }
}
// 결과: 100,000을 기대했지만, 실제로는 훨씬 적은 값이 나옴

2. 해결사: Mutex (Mutual Exclusion)

Mutex는 한 번에 하나의 코루틴만 특정 코드 블록(Critical Section)을 실행할 수 있도록 보장합니다.

핵심 특징

val mutex = Mutex()
var counter = 0

mutex.withLock {
    counter++ // 안전하게 보호됨
}

딥다이브 포인트


3. Mutex vs Atomic (AtomicInteger)

단순한 숫자 카운팅이라면 AtomicInteger가 훨씬 효율적일 수 있습니다.

구분 Mutex Atomic
방식 잠금(Locking) 기반 잠금 없는(Lock-free) CAS 방식
속도 코루틴 중단 오버헤드 발생 CPU 레벨 연산으로 매우 빠름
범위 여러 줄의 복잡한 로직 보호 단일 변수의 값 변경

4. 실무 가이드: 언제 무엇을 쓸까?

Mutex를 써야 할 때

Atomic을 써야 할 때


5. 벤치마크 결과 (Benchmark Results)

실제 안드로이드 에뮬레이터에서 100개의 코루틴이 각각 10,000번씩(총 1,000,000번) 숫자를 증가시킨 실험 결과입니다.

방식 결과 정확도 소요 시간 (ms) 비고
No Sync ❌ (유실 발생) ~4ms 가장 빠르지만 데이터 일관성 없음
Mutex (정확함) ~3548ms 안전하지만 일시 중단 오버헤드가 큼
AtomicInteger (정확함) ~15ms 단순 연산에서 가장 압도적인 효율

분석: Mutex는 코루틴의 일시 중단(Suspension)과 재개 과정이 포함되어 Atomic 방식보다 훨씬 느린 결과를 보였습니다. (실험 환경에 따라 차이가 있을 수 있습니다.)


6. 마치며 (안드로이드 개발자의 시점)

서버 환경뿐만 아니라 안드로이드에서도 Mutex는 중요합니다. 앱의 데이터 무결성을 지키고, 경쟁 상태로 인한 예기치 못한 크래시를 방지하기 위해 반드시 숙지해야 할 개념입니다.

아래는 직접 작성한 예제 앱을 통해 도출한 실제 벤치마크 캡처 화면입니다.

Mutex 벤치마크 결과 화면


7. 참고 자료 (References)