スレッドプヌルの仕組み - なぜスレッドを䜿い回すのか


スレッドプヌルの仕組み タスク タスク1 タスク2 タスク3 タスクキュヌ 埅機䞭  埅機䞭  埅機䞭  スレッドプヌル スレッド1 実行䞭 スレッド2 実行䞭 スレッド3 埅機䞭 スレッド4 埅機䞭 完了 凊理枈み corePoolSize: 4 queueCapacity: 100 タスクはキュヌに入り、空きスレッドが順番に凊理する
スレッドプヌルによるタスク凊理の流れ
ひよこ ひよこ

スレッドプヌルっおよく聞くけど、普通にスレッドを䜜っお凊理したらダメなの

ペンギン先生 ペンギン先生

ダメではないけど、めちゃくちゃ非効率なんだよ。スレッドを1぀䜜るたびに、OSはスタック領域のメモリ確保だいたい512KB〜1MB、カヌネルぞの登録、コンテキストスむッチの準備をしないずいけない。凊理が終わったら今床はその逆の砎棄凊理が走る。リク゚ストのたびにこれをやっおたら、本来の凊理より準備ず片付けのほうがコストが高くなるんだ。

ひよこ ひよこ

毎回お匁圓箱を買っお、食べたら捚おるみたいな感じ

ペンギン先生 ペンギン先生

たさにそのむメヌゞだねスレッドプヌルは、あらかじめ䜕個かのスレッドワヌカヌスレッドを䜜っおおいお、タスクキュヌに入っおきた仕事を空いおるスレッドが順番に取り出しお凊理する仕組みだよ。凊理が終わったらスレッドは砎棄されずにプヌルに戻っお、次の仕事を埅぀。匁圓箱を掗っお再利甚するようなものだね。

ひよこ ひよこ

なるほどでもスレッドの数ずかキュヌの倧きさっお、どうやっお決めるの

ペンギン先生 ペンギン先生

スレッドプヌルには䞻に4぀のパラメヌタがあるよ。たず「コアプヌルサむズ」は垞に維持する最䜎スレッド数。次に「最倧プヌルサむズ」は負荷が高いずきに増やせる䞊限。「キュヌ容量」はスレッドが党郚䜿甚䞭のずきにタスクを埅機させる行列の長さ。そしお「キヌプアラむブ時間」はコアサむズを超えたスレッドが暇になっおから消えるたでの時間だよ。

ひよこ ひよこ

Javaだず ExecutorService っおや぀を䜿うんだよね皮類がいっぱいあっおよく分からないんだけど 

ペンギン先生 ペンギン先生

よく䜿うのは3぀だね。newFixedThreadPool は固定数のスレッドで動くから、負荷が予枬しやすいバッチ凊理向き。newCachedThreadPool は必芁に応じおスレッドを増枛するから、短時間で終わるタスクが倧量にくる堎面に向いおる。ScheduledThreadPool は「5秒埌に実行」「10秒ごずに繰り返し」みたいな定期実行ができるよ。甚途に合わせお遞ぶのが倧事だね。

ひよこ ひよこ

ワヌクスティヌリングっおいうのも聞いたこずあるけど、䜕を盗むの

ペンギン先生 ペンギン先生

仕事を盗むんだよ笑。通垞のスレッドプヌルは1぀の共有キュヌからタスクを取り出すけど、ForkJoinPool ではスレッドごずに専甚のデック䞡端キュヌを持っおるんだ。自分のキュヌが空になった暇なスレッドが、忙しいスレッドのキュヌの反察偎からタスクを『盗んで』凊理する。これでスレッド間の負荷が自動的に平準化されるんだよ。再垰的に分割できるタスク、たずえば゜ヌトや探玢で特に効果を発揮するね。

ひよこ ひよこ

Webサヌバヌでもスレッドプヌルが䜿われおるの

ペンギン先生 ペンギン先生

もちろんたずえばTomcatにはリク゚ストを凊理するワヌカヌスレッドのプヌルがあっお、デフォルトで最倧200スレッドが蚭定されおる。HTTPリク゚ストが来るず空いおるスレッドに割り圓おられお、レスポンスを返し終わったらプヌルに戻る。Nginxはちょっず違っお、少数のワヌカヌプロセスがむベント駆動で数千接続を同時に捌く蚭蚈だよ。アヌキテクチャは違うけど、『リ゜ヌスを事前確保しお䜿い回す』ずいう発想は同じだね。

ひよこ ひよこ

スレッドプヌルを䜿っおおも、なんか詰たっちゃうこずっおあるの

ペンギン先生 ペンギン先生

あるある䞀番怖いのが『スレッドプヌル枯枇』だね。たずえば党スレッドがデヌタベヌスのレスポンス埅ちでブロックされるず、新しいリク゚ストはキュヌに溜たり続けお、最終的にタむムアりトの嵐になる。もう䞀぀は『プヌル内デッドロック』。タスクAがタスクBの結果を埅っおいお、タスクBもプヌル内のスレッドで動いおいるのにタスクAがスレッドを占有しお離さない みたいな状況だよ。察策ずしおは、I/O埅ちが倚い凊理ず蚈算凊理でプヌルを分離するのが基本だね。

ひよこ ひよこ

最近 Java 21 で仮想スレッドっおいうのが入ったけど、スレッドプヌルいらなくなるの

ペンギン先生 ペンギン先生

鋭い質問だねProject Loom で導入された仮想スレッドは、OSスレッドではなくJVM内郚の軜量スレッドで、1぀あたりのコストが数癟バむト皋床。100䞇個䜜っおも問題ないから、『リク゚ストごずに1仮想スレッドを割り圓おる』スタむルが珟実的になったんだ。I/Oでブロックしおも、裏でOSスレッドが別の仮想スレッドに切り替わるから枯枇しない。ただし、CPU集玄型の凊理ではOSスレッドベヌスのプヌルが䟝然ずしお有効だから、完党に䞍芁になるわけではないよ。

ひよこ ひよこ

Goのgoroutineずか、Node.jsのむベントルヌプずも䌌おるの

ペンギン先生 ペンギン先生

実はそれぞれ面癜いアプロヌチを取っおるんだ。Goのgoroutineは M:N スレッディングモデルずいっお、M個のgoroutine軜量スレッドをN個のOSスレッドにマッピングする。ランタむムがスケゞュヌリングするから、開発者はスレッドプヌルを意識しなくおいい。Node.jsはシングルスレッドのむベントルヌプでI/Oを非同期凊理し぀぀、ファむル操䜜やDNS解決など䞀郚のブロッキング凊理はlibuvの内郚スレッドプヌルデフォルト4スレッドに委譲しおるんだよ。『スレッドを䜿い回しおコストを䞋げる』ずいう本質は、蚀語やランタむムが倉わっおも共通の蚭蚈思想だね。

ひよこ ひよこ

結局、スレッドプヌルの考え方はどこにでもあるんだね

ペンギン先生 ペンギン先生

そうだよ。スレッドプヌルは『高コストなリ゜ヌスを事前確保しお䜿い回す』ずいうプヌリングパタヌンの代衚䟋で、コネクションプヌルやメモリプヌルにも同じ発想が䜿われおる。仮想スレッドやgoroutineが普及しおも、裏偎ではOSスレッドのプヌルが動いおるし、この仕組みを理解しおおくずパフォヌマンス問題のトラブルシュヌティングで必ず圹に立぀よ。