Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Virtual Threads - 導入の背景と、効果的な使い方 -

Virtual Threads - 導入の背景と、効果的な使い方 -

2023.06.04 JJUG CCC セッション資料

Yuichi.Sakuraba

June 04, 2023
Tweet

More Decks by Yuichi.Sakuraba

Other Decks in Technology

Transcript

  1. 2004 2011 2014 2023 1995 Java 5 Java 7 Java

    8 Java 21 Java 1.0 Single Core の時代 Thread = OSスレッドのラッパー Threadの切替 : スレッドのコンテキストスイッチ OS Threadオブジェクト生成: 重い & メモリ消費大
  2. OS Thread Java Thread JVM Stack new Thread(new Runnable() {

    public void run() { foo(); } }).start(); run() Frame void foo() { bar(); } foo() void bar() { ... } bar()
  3. OS Thread Java Thread JVM Stack new Thread(new Runnable() {

    public void run() { foo(); } }).start(); run() Frame void foo() { bar(); } foo() void bar() { ... } bar() run() foo() bar() Operand Stack Local Var.
  4. OS Thread Java Thread JVM Stack run() foo() bar() Operand

    Stack Local Var. のコンテキストスイッチ Thread のスレッドの状態 OS オブジェクトの状態 Thread JVM Stack + + の退避・復帰
  5. 2004 2011 2014 2023 1995 Java 5 Java 7 Java

    8 Java 21 Java 1.0 Single Core の時代 Thread = OSスレッドのラッパー Threadの切替 : スレッドのコンテキストスイッチ OS Threadオブジェクト生成: 重い & メモリ消費大 重い & メモリ消費大 コンテキストスイッチをなるべく発生させない スレッドスケジューリング
  6. 2011 2014 2023 Java 7 Java 8 Java 21 Java

    1.0 1995 2004 Java 5 Multi Core 黎明期 Concurrency Utilities スレッドとタスクの分離 スレッドプール導入 Executors.newFixedThreadPool 応答時間重視 Executors.newCachedThreadPool スループット重視 リクエスト増大 スループット頭打ち Thread per Request/Task
  7. 2011 2014 2023 Java 7 Java 8 Java 21 Java

    1.0 1995 2004 Java 5 Multi Core 黎明期 public class ThreadPoolExecutor extends AbstractExecutorService { ... private final class Worker extends AbstractQueuedSynchronizer implements Runnable { Worker(Runnable firstTask) { setState(-1); // inhibit interrupts until runWorker this.firstTask = firstTask; this.thread = getThreadFactory().newThread(this); } ... Apache Tomcat 例
  8. 2014 2023 Java 8 Java 21 Java 1.0 1995 2004

    Java 5 2011 Java 7 Big Dataの時代 Fork/Join Framework 分割統治法 タスクを細粒度に分割して処理 Work-Stealing タスクスケジューラー スレッドごとにタスクキュー キューが空の時は他のスレッドからタスクを盗む 効率的なタスクスケジューリング
  9. 業務タスクの特徴 Comp. I/O C I/O I/O C C 通信、 DBアクセスなど

    I/O処理が多い I/O処理は計算処理に対して多大な時間を要する I/Oの待ち時間が長い その中でも I/O待ちの間 CPUが遊んでしまう I/O待ちの間に他の処理を行いたい
  10. 2023 Java 21 Java 1.0 1995 2004 Java 5 2011

    Java 7 2014 Java 8 Project Lambda ラムダ式の導入 処理を関数として記述 CompletableFuture Reactive Programming 非同期処理を関数で記述 CompletableFuture Reactive Programming ex. Spring WebFlux Oracle Helidon SE 処理を非同期実行することで スループット向上 I/O とはいうものの 従来の逐次的な記述と考え方が異なる ... 例外処理が ... デバッグが ...
  11. Java 1.0 1995 2004 Java 5 2011 Java 7 2014

    Java 8 2023 Java 21 従来の逐次的な記述で 例外処理をあつかいやすく デバッグもやりやすいままで スループットを向上させたい Virtual Threads
  12. Virtual Threads Platform Threadとタスクの仲介 Platform Thread タスクからは と区別がつかない Platform Thread

    にマウントして実行 Carrier Thread マウントしたスレッドを と呼ぶ 積極的なコンテキストスイッチ 限定継続を導入してコンテキストスイッチの高速化 Work-Stealingによるスレッドスケジューリング 一貫性のあるスタックトレース Carrier Threadが変わってもスタックトレースが切れない
  13. Platform Thread Platform Thread ...... Virtual Thread Task public int

    read(ByteBuffer buf) throws IOException { ... readLock.lock(); try { ... configureSocketNonBlockingIfVirtualThread(); n = IOUtil.read(fd, buf, -1, nd); if (blocking) { while (IOStatus.okayToRetry(n) && isOpen()) { park(Net.POLLIN); n = IOUtil.read(fd, buf, -1, nd); } } ... sun.nio.ch.SocketChannelImpl 例
  14. void park() { ... boolean yielded = false; setState(PARKING); try

    { yielded = yieldContinuation(); } finally { ... } ... } java.lang.VirtualThread private boolean yieldContinuation() { ... unmount(); try { return Continuation.yield(VTHREAD_SCOPE); } finally { mount(); ... } } 限定継続
  15. VT使う必要なし or CompletableFutureゴリゴリ書ける Spring Boot, Jakarta EE など FWの対応待ち その他

    使ってない or ボトルネックは? データ量 / 計算量 ParallelStream Fork/Join FW Vector API File Memory Map DB 通信 / VirtualThread
  16. スレッドの注意点 Virtual Threads 特に synchronizedをなるべく使わないようにする 1. synchronized = モニタロック リソースへのアクセスを

    1 つのスレッドだけに限定 他のスレッドをブロックする ボトルネックになりやすい Virtual Threadでは Carrier Threadをブロックしてしまう
  17. スレッドの注意点 Virtual Threads 特に synchronized を使わないようにするには イミュータブルクラスの活用 record 型の利用 状態が変化しなければ複数スレッドからもアクセス可

    処理結果の受け渡しがある場合 Callableを使う どうしてもロックが必要であれば synchronizedではなくReentrantLock クラスを使う java.util.concurrent.locksにロッククラスあり 他にも
  18. スレッドの注意点 Virtual Threads 特に ThreadLocal を使わない 2. ThreadLocal はミュータブル &

    ライフタイムが不明確 Virtual Threads ThreadLocalをサポートしているが は スケールしないためボトルネック化 ScopedValueで代用する Java 21 Preview JEP では ただし FWが ThreadLocal を使っている場合 対応してくれるまで待ちましょう
  19. スレッドの注意点 Virtual Threads 特に スレッドを使いまわさない 3. Virtual Threadsは使い捨て ExecutorService スレッドのプールは

    にまかせる タスクを記述することに集中しよう クラスの 4. Thread APIをもう 1 度チェック @Deprecated(forRemoval=true)なメソッド stop, suspend, resumeなど