【比較】スレッド・コルーチン・グリーンスレッドの違い¶
概要¶
「(OS)スレッド」「コルーチン」「グリーンスレッド」は、いずれも“複数の処理を並行に進める実行単位”ですが、誰がスケジュールするかといつ切り替わるかが決定的に違います。OSスレッドはカーネルがスケジュールし、任意のタイミングで横取り(プリエンプティブ)されます。コルーチンは自分から await/yield 等で協調的に中断・再開する仕組みで、スレッドを横取りしません。グリーンスレッド(軽量スレッド/仮想スレッド/goroutine 等)は、言語ランタイムがユーザー空間でスケジュールし、多数(M)の論理スレッドを少数(N)のOSスレッドへ多重化(M:N)します。
この3つは「軽量な並行」という共通の文脈で語られるため混同されがちですが、特にコルーチンとグリーンスレッドはしばしば同一視されます。本記事では OpenJDK(JEP 444)、Oracle 公式ドキュメント、Go 公式 “Effective Go”、Python asyncio、Kotlin 公式ドキュメントを一次情報として、スケジューリングの主体と切り替え方式で正確に分けます。
例えるなら:会議室の使い方¶
OSスレッドは「タイマー(OS)が一定時間で強制的に話者を交代させる司会者付き会議」。コルーチンは「話者が“ここで一旦お返しします(await)”と自発的に譲るまで交代しない会議」で、譲らない人がいると進みません。グリーンスレッドは「受付(言語ランタイム)が大勢の参加者を、少数の実際の会議室に上手に割り当てて回す」運用です。
詳細比較¶
| 項目 | OSスレッド (Thread) | コルーチン (Coroutine) | グリーンスレッド/仮想スレッド (Green/Virtual Thread) |
|---|---|---|---|
| スケジューラ | OSカーネル | プログラム自身(イベントループ等) | 言語/VMランタイム(ユーザー空間) |
| 切り替え方式 | プリエンプティブ(任意時点で横取り) | 協調的(await/yield 等の中断点でのみ) |
ランタイム制御(協調的中断点で切替えることが多い) |
| OSスレッドとの対応 | 1:1(カーネルスレッドに対応) | スレッドに非束縛(1スレッド上で多数を回せる) | M:N(多数の論理スレッドを少数のOSスレッドに多重化) |
| 代表例 | POSIX threads, Java platform thread | Python async/await, Kotlin coroutine, C# async |
Go goroutine, Java virtual thread (JEP 444), Erlang プロセス |
| 生成コスト/規模 | 重い(数千規模で資源逼迫しやすい) | 非常に軽い(数百万規模も可) | 軽い(Java 仮想スレッドや goroutine は大量生成可) |
| スタック | OS割当の比較的大きな固定スタック | スタックレス/スタックフルは実装による | 小さく可変(例: goroutine は小さく始まり伸長) |
| ブロッキング呼び出しの影響 | OSが他スレッドへ切替(並行は保たれる) | 非同期非対応のブロッキング呼び出しはイベントループ全体を止める | ランタイム対応のブロッキングは“unmount”され担体OSスレッドは解放(未対応操作は担体ごとブロックし得る) |
| 速度 vs スケール | 基準 | 主にI/O並行の効率化 | 「速くはならない。スケール(スループット)のための仕組み」(Oracle) |
補足:
- OSスレッド:Oracle のドキュメントは仮想スレッドとの対比で、従来のプラットフォームスレッドがOSスレッドに 1:1 で対応する重い単位であることを前提に説明しています。横取り(プリエンプション)はカーネルが行います。
- コルーチン:Python
asyncioは「コルーチンはasync/await構文で宣言し、イベントループは協調スケジューリングで一度に1タスクを実行する」と述べます。Kotlin 公式も「コルーチンはスレッドをブロックする代わりに中断できる」「特定スレッドに束縛されず、別スレッドで再開でき、スレッドより遥かに軽い」と明言します。Kotlin 公式の例では 50,000 個でコルーチン約 500MB に対し JVM スレッドは最大約 100GB とされます。 - グリーンスレッド/仮想スレッド:JEP 444 は仮想スレッドを「OSではなく JDK が提供する軽量スレッド」とし、M:N スケジューリング(多数の仮想スレッドを少数のOSスレッドで実行)、スケジューラは work-stealing の
ForkJoinPool、ブロッキング操作の大半で仮想スレッドが unmount され担体(carrier)と背後のOSスレッドが解放される、と定めます。Oracle は「仮想スレッドはより速いスレッドではない。コードが速く走るわけではなく、スケール(高スループット)のために存在する」と明記。Go の “Effective Go” は goroutine を「同一アドレス空間で他の goroutine と並行実行される関数。軽量でスタック確保程度のコスト。複数のOSスレッドに多重化され、1つがI/O待ちでブロックしても他は走り続ける」と定義します。
よくある誤解¶
-
「コルーチンとグリーンスレッドは同じもの」は誤り。 似て見えますが層が違います。コルーチンは“中断・再開できる計算”という言語機能で、典型的には1スレッド上でイベントループが協調スケジュールします(Python
asyncio)。グリーンスレッド/仮想スレッドはランタイムが多数の論理スレッドを少数のOSスレッドに M:N で割り当てる“スレッド風の実行単位”です(JEP 444 の仮想スレッド、Go の goroutine)。両者を実装基盤に使う言語もありますが、概念としては別レイヤーであり同一視は不正確です。 -
「コルーチン/グリーンスレッドなら何でも勝手に並行化されるので、重い処理を普通に呼んでよい」は誤り。 協調的モデルでは中断点でしか切り替わりません。
async対応でないブロッキング処理(同期I/O・重いCPU処理)をコルーチン内で素直に呼ぶと、イベントループ全体が止まります(asyncioは別スレッド実行のasyncio.to_thread()を用意)。仮想スレッドでも、JDK が unmount 対応していない一部の操作(一部のファイル操作やObject.wait()など)は担体OSスレッドごとブロックし得ます(これらのピン留め挙動は Java 21 / JEP 444 時点の仕様であり、例えばsynchronizedによるピン留めは後の JDK(JEP 491, Java 24)で解消されています)。 -
「グリーンスレッド/仮想スレッドはOSスレッドより“速い”」は誤り。 Oracle は「仮想スレッドはより速いスレッドではない。コードが速く走るわけではなく、スケール(高スループット)のために存在し、低遅延(速度)のためではない」と明言します。利点は1コードあたりの速度ではなく、膨大な数の並行タスクを低資源で捌けるスケーラビリティです。
-
「コルーチンはOSスレッド上で動く軽量なOSスレッドだ」は誤り。 コルーチンはOSがスケジュールする実行単位ではありません。Kotlin 公式の通り「特定スレッドに束縛されず、ある時点でスレッドAで中断し別スレッドで再開し得る」協調的な計算であり、横取りもOSが行いません。OSスレッド/カーネルスケジューリングと混同しないことが重要です。
実務での選び分け¶
- OSによる公平なプリエンプションが要る/既存のブロッキングAPIをそのまま使う薄い並行 → OSスレッド(プラットフォームスレッド)。ただし数千規模で資源を圧迫しやすい点に注意。
- 大量のI/O待ちを1〜少数スレッドで効率良く捌きたい(Webクライアント、ソケット多重化) → コルーチン(
async/await)。ただしブロッキング処理はオフロード(例:asyncio.to_thread)。 - 「1リクエスト=1スレッド」の素直な同期スタイルのまま莫大な同時接続を捌きたい → グリーンスレッド/仮想スレッド(Java 仮想スレッド, Go goroutine)。コードは同期的に書け、ランタイムが M:N で多重化。
- CPUバウンドな重い計算を並列化したい → どれも“それ単体”では解にならない。OSスレッド/プロセスで実コアに分散する(協調モデルやGIL等の別制約も考慮)。
ひとことまとめ¶
OSスレッドは「カーネルがプリエンプティブにスケジュールする 1:1 の重い実行単位」、コルーチンは「中断点で協調的に譲るスレッド非束縛の言語機能」、グリーンスレッド/仮想スレッドは「ランタイムが M:N で多重化する軽量スレッド」であり、特にコルーチンとグリーンスレッドは別レイヤーなので同一視しないことが要点です。
出典・参考¶
- JEP 444: Virtual Threads — OpenJDK — 仮想スレッドの定義、M:N、ForkJoinPool スケジューラ、unmount、Java 21 で恒久機能
- Virtual Threads — Oracle Java SE 21 ドキュメント — 担体(carrier)/マウント、ブロッキングI/Oでの解放、「速くはならずスケールのため」の明言
- Effective Go — go.dev 公式 — goroutine の定義(軽量・小さく伸長するスタック・OSスレッドへ多重化・I/Oブロック時も他が走る)
- Coroutines and Tasks — Python
asyncio公式ドキュメント —async/awaitでのコルーチン定義、協調スケジューリング、asyncio.to_threadによるブロッキング処理のオフロード - Coroutines basics — Kotlin 公式ドキュメント — コルーチンは中断する(ブロックしない)/スレッド非束縛/スレッドより遥かに軽い