← Spring コース
53. 1リクエスト1スレッドと仮想スレッド
既定では、 の1本が1つのリクエストを最初から最後まで受け持ちます。の中で Thread.sleep を呼べば、そのごと止まります。作業スレッドの上限は で、既定は 200 です。1秒眠るだけのハンドラへ同時に 400 件送ると、8回とも全体で約 2.1〜2.2 秒かかり、数えた5回では毎回ちょうど 200 件が後回しになっていました。
は、 全体で共有するの上に JVM が載せて動かすスレッドです(Java 21 以降)。=true にすると受け持ちがこれに替わり、同じ 400 件が8回中5回は約 1.1〜1.2 秒で終わりました。Node 24 の await でも約 1.1 秒でした。
時計が1秒進むまで回り続けるハンドラでは、結果が逆でした。10 CPU の環境で、Node は 20 件で約 20 秒、仮想スレッドは 20 件で約2秒、既定のスレッドは 40 件でも約 1.2 秒です。これは優劣ではなく、待ち方をどこに置いたかの違いです。なお、回り続ける処理は決まった量の計算ではないので、計算の速さは比べていません。仮想スレッドでも8回中3回は約 2.1〜2.2 秒かかり、原因は確かめていません。
点線の付いた語はタップすると説明が出ます。用語集を開く →
Tomcat の既定1リクエストに OS のスレッド1本(上限 200)
仮想スレッド1リクエストに仮想スレッド1本。JVM 全体で共有するスレッドの上で動く
Node 24JavaScript を動かすスレッドは1本。await で手放す
1秒待つ処理を同時 400 件
約 2.1〜2.2 秒8回とも。200 件が後回し
約 1.1〜1.2 秒8回中5回。残り3回は約 2.1〜2.2 秒
約 1.1 秒3回とも
時計が1秒進むまで回り続ける処理を同時 20 件
約 1.1 秒2回とも。40 件でも約 1.2 秒
約 2 秒2回とも。40 件では約4秒
約 20 秒2回とも。1件ずつ順番に
- ・回り続ける処理は「時計が1秒進むまでループする」だけで、決まった量の計算ではありません。計算をこなす速さの比較にはなりません
- ・仮想スレッドでは、回り続ける処理が 10 件(この環境の CPU 数)ずつ進んだように見えました。1.5 秒を超えたのは 20 件中 10 件、40 件中 30 件です
- ・仮想スレッドの約 2.1〜2.2 秒の回では、遅かったのは一部のリクエスト(57 件・14 件など)で、原因は確かめていません
- ・設定の説明文には、仮想スレッドを有効にすると threads.max は効かないと書かれています
サンプルコード(フレームワーク環境が必要なため表示のみ)
// ThreadsApp.java(抜粋)— Spring Boot 4.1.1
@RestController
class SleepController {
@GetMapping("/thread")
String thread() {
Thread t = Thread.currentThread();
return "toString=" + t + " name=" + t.getName() + " virtual=" + t.isVirtual();
}
@GetMapping("/sleep")
String sleep() throws InterruptedException {
Thread.sleep(1000); // このリクエストを受け持つスレッドが1秒止まる
Thread t = Thread.currentThread();
return t.isVirtual() ? "virtual " + t.getName() : t.getName();
}
}
// 既定で起動して GET /thread(スレッド番号は実行ごとに変わる)
// toString=Thread[#41,http-nio-8080-exec-1,5,main] name=http-nio-8080-exec-1 virtual=false
//
// --spring.threads.virtual.enabled=true で起動して GET /thread
// toString=VirtualThread[#46,tomcat-handler-0]/runnable@ForkJoinPool-1-worker-1 name=tomcat-handler-0 virtual=true
//
// /sleep へ同時 400 件(全体の完了時間。10 CPU の Docker、クライアントも同じマシン、ウォームアップなし)
// 既定(上限 200) : 8回とも約 2.1〜2.2 秒(2088〜2209 ms)
// 仮想スレッド : 8回中5回が約 1.1〜1.2 秒(1117〜1158 ms)、3回が約 2.1〜2.2 秒(2139〜2185 ms)
図やコードに出てくる用語(タップで説明)
サンプルコードで身につける(5本) →
学んだ概念を、素朴な例から実務寄りの例まで5つの具体例で確認します。
公式ドキュメントで詳しく ↗