並行性 — ロック・デッドロック・同期/非同期・ブロッキング/ノンブロッキング
並行性 — ロック・デッドロック・同期/非同期・ブロッキング/ノンブロッキング
🎯 このレッスンを読み終えたら
このレッスンを読み終えると、以下の3つのトピックについて自信を持って説明できるようになります。
- ▸✅ Mutex vs Semaphore vs Monitor
- ▸✅ Race Condition + デッドロックの4条件
- ▸✅ 同期 vs 非同期 + ブロッキング vs ノンブロッキング の4つの組み合わせ
学習目標をチェックリストとして手元に置き、すべてに答えられたらレッスンを閉じてください。
Race Condition — 同時変更の悲劇
一言で言えば: 2つのスレッドが同じデータを同時に変更 → 結果が予測不能。
例:
理由: counter + 1 は3つの命令から成る:
1. read counter → register
2. register + 1
3. register → counter
Aがステップ1・2を実行した後にBがステップ1を実行すると → 両方が0を読んで+1し、結果として1しか保存されない。
解決策:
よくあるrace conditionの落とし穴:
- ▸❌ デバッガを起動すると消えるバグ (Heisenbug) — デバッガがタイミングを変えてしまう
- ▸❌ 「自分のマシンでは動く」 — CPUコア数や負荷が異なる
- ▸✅ 必ず並行性テストを実施する (
-raceフラグ・ThreadSanitizer)
Mutex — 最も一般的なロック
Mutex (MUTual EXclusion): 一度に1つのスレッドのみクリティカルセクションに進入できる。
使用フロー:
言語別Mutex:
Mutexのよくある間違い:
ReadWriteLock: 読み取りは複数同時 + 書き込みは排他 → 読み取りが多いワークロードに有利。
Optimistic Lock: DBでよく使用。競合時にリトライ (CASと類似)。
デッドロック — 永遠の待機
4つの必要条件 (すべてが揃うとデッドロック):
1. 相互排除 (Mutual Exclusion) — リソースは一度に1スレッドのみが保持できる
2. 占有と待機 (Hold and Wait) — リソースを保持しながら別のリソースを待つ
3. 非横取り (No Preemption) — リソースを強制的に取り上げられない
4. 循環待機 (Circular Wait) — A→B→C→Aの循環
古典的な例: 2つのスレッドが同じ2つのロックを異なる順序で取得:
→ スレッド1はAを保持してBを待つ / スレッド2はBを保持してAを待つ → 永遠に進まない
6つの解決策:
デッドロックのデバッグ:
- ▸Java:
jstack <PID>→ 「Found one Java-level deadlock」を自動検出 - ▸Python:
faulthandler+ thread dump - ▸Go:
go run -race(race detector) - ▸DB: PostgreSQLが自動検出してvictimトランザクションをabort
同期 vs 非同期 · ブロッキング vs ノンブロッキング
4つの組み合わせ (よく混同されるため個別に理解する):
同期/非同期 = 完了通知の方式
ブロッキング/ノンブロッキング = 呼び出し元が処理を続けられるかどうか
Linux I/Oモデル 4種類:
epollの動作 (Linuxの効率的なI/Oマルチプレキシング):
1. epoll_create() — インスタンス生成
2. epoll_ctl(ADD) — 監視するfdを登録
3. epoll_wait() — イベント発生までブロック
4. fdを処理 → 再びwait
Node.jsとNginxが速い理由 = シングルスレッド + epollイベントループ → コンテキストスイッチなし。
🤖 AIへのリクエスト例
このレッスンの概念を知っていれば、AIに具体的な指示を出せます。
- ▸「このメソッドの並行性の問題を分析して、synchronizedとReentrantLockのどちらが適切か推薦してください。」
- ▸「このコードにデッドロックの可能性があります。リソース取得順序を基に診断してください。」
- ▸「このタスクをCompletableFuture + thenCombineで並列処理してください。」
なぜこれでトークンが減るのか
「ロック / デッドロック / volatile / atomic」という語彙を知っていれば、AIの返答が即コードで返ってきます。知らなければ「並行性とは...」という説明から始まります。