0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Kotlin】Kotlin Coroutinesで理解する Mutex(ミューテックス)

Posted at

概要

Kotlin の Mutex(ミューテックス)は、
複数のコルーチンが同じリソースを同時に扱わないようにするための排他ロック機構です。

スレッドレベルのロック(synchronized)とは異なり、
軽量・非ブロッキング・サスペンド対応 が特徴です。

一言で言うと
「コルーチンの世界における synchronized」


1. 基本構文:MutexwithLock

Kotlin Coroutines の Mutexkotlinx.coroutines.sync パッケージに含まれています。

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*

fun main() = runBlocking {
    val mutex = Mutex()
    var counter = 0

    val jobs = List(1000) {
        launch {
            mutex.withLock {
                counter++
            }
        }
    }

    jobs.joinAll()
    println("Counter = $counter")
}

ポイント:

  • Mutex() はスレッドではなくコルーチン単位での排他
  • withLock { ... } ブロック内は同時に1コルーチンしか実行されない
  • lock() / unlock() でも操作可能(低レベルAPI)

2. lock() / unlock() の手動制御

withLock {} は便利な構文ですが、
明示的に制御したい場合は lock() / unlock() を使えます。

val mutex = Mutex()
var counter = 0

val job = launch {
    mutex.lock()
    try {
        counter++
    } finally {
        mutex.unlock()
    }
}

withLock の糖衣構文

mutex.withLock {
    counter++
}

これは上記コードと同義です。


3. Mutexsynchronized の違い

比較項目 synchronized Mutex
スコープ スレッド コルーチン
実行ブロック ブロッキング サスペンド
実装レイヤ JVM(OSレベル) kotlinx.coroutines
デッドロック回避 難しい 比較的安全(構造化並行性)
可搬性 Java限定 KMP(マルチプラットフォーム)対応

つまり:

  • Mutex軽量で非ブロッキング
  • スレッドを止める代わりに、コルーチンを中断(suspend) させる

4. 典型的な適用場面

カウンタ・共有リストの更新

val mutex = Mutex()
val sharedList = mutableListOf<Int>()

coroutineScope {
    repeat(5) { i ->
        launch {
            mutex.withLock {
                sharedList.add(i)
            }
        }
    }
}

println(sharedList)

ファイル書き込み・ログ出力

val fileMutex = Mutex()

suspend fun safeLog(line: String) {
    fileMutex.withLock {
        println("[${Thread.currentThread().name}] $line")
        // 実際は FileWriter.append(line)
    }
}

5. Mutex vs Channel

概念 用途 挙動
Mutex 共有リソースのロック 「同時アクセスを防ぐ」
Channel データの送受信 「データを順番に流す」

選び方:

  • 「共有状態(変数やリスト)を守りたい」→ Mutex
  • 「値を順番に渡したい」→ Channel

6. tryLock() による非ブロッキングロック

ブロック(サスペンド)せずにロックを試みたい場合:

if (mutex.tryLock()) {
    try {
        println("Locked successfully!")
    } finally {
        mutex.unlock()
    }
} else {
    println("Already locked, skipping...")
}

用途:

  • タイムアウト付きリソースアクセス
  • 同時実行をスキップしたいケース

7. MutexwithTimeout の組み合わせ

時間制限付きロックも可能です。

import kotlinx.coroutines.withTimeout

val mutex = Mutex()

suspend fun timedWork() {
    withTimeout(500L) {
        mutex.withLock {
            println("Acquired lock within timeout!")
            delay(1000L)
        }
    }
}

500ms 以内にロックを取得できなければ TimeoutCancellationException が発生します。
これにより「デッドロック回避」や「リソース待機の制限」が可能。


8. 実戦例:構造化並行性 × Mutex

複数のワーカーが共有カウンタを安全に扱うケース:

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*

suspend fun safeCounterExample() = coroutineScope {
    val mutex = Mutex()
    var counter = 0

    val workers = List(5) {
        launch {
            repeat(100) {
                delay(10)
                mutex.withLock { counter++ }
            }
        }
    }

    workers.joinAll()
    println("Final counter = $counter")
}

fun main() = runBlocking {
    safeCounterExample()
}

結果:
Mutex がなければレースコンディションで値が欠損するが、
withLock により 確実に100×5=500 が出力される。


9. FlowやActorとの連携

Mutexは 状態保護(State Protection) に使い、
FlowやActorは**データフロー制御**に使います。

例:StateFlow 更新を排他制御する

val mutex = Mutex()
val _state = MutableStateFlow(0)

suspend fun incrementSafely() {
    mutex.withLock {
        _state.value++
    }
}

StateFlow は複数コルーチンから書き換え可能なので、
安全な更新には Mutex が最適。


まとめ

概念 説明
Mutex コルーチン用の非ブロッキングロック
withLock {} ロックを自動開放する安全ブロック
tryLock() 即時取得を試みる
withTimeout 時間制限付きロック
lock() / unlock() 低レベル制御
Channelとの違い Channelはデータ伝達、Mutexは状態保護
使用目的 共有リソースの排他、データ整合性維持

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?