1,000 万の仮想スレッドを開始するにはどれくらい時間がかかりますか? プラットフォームのスレッドはいくつ必要ですか?

以前、Java の新機能コラムで、Java 21 で正式にリリースされた仮想スレッドを簡単に紹介しました

昨日たまたまこの内容を解説したビデオを拝見しましたが、とても良かったのでDDが翻訳して公開しましたので、興味のある方はご覧ください。仮想スレッドについて詳しく学ぶことができます。

仮想スレッドとは何ですか? 一度に 1,000 万個がアクティベートされたらどうなるでしょうか?

この動画では翻訳と吹き替え処理にChromeプラグイン「Youtube Chinese Dubbing」を使用していますので、普段YouTubeで最先端の動画を視聴している方もインストールしておくと学習効率が効果的に上がります

現在の環境が動画視聴に適していない場合は、以下のテキストコンテンツからも学習できます。以下の内容は、動画の内容をもとに私がより簡潔にまとめたものです。

仮想スレッドとは

仮想スレッドは Java 同時実行の分野に追加された新しい概念ですが、仮想スレッドは正確には何に使用されるのでしょうか?

JEP の内容によると、仮想スレッドは軽量のスレッドであり、高スループットのアプリケーションの作成、保守、監視の作業負荷を大幅に軽減するのに役立ちます。その実装目標には次のものが含まれます。

  1. リクエストごとのスレッド形式で作成されたプログラムは、最適に近いハードウェア使用率で拡張できます。

リクエストごとに 1 つのスレッドとは何ですか?

HTTP サーバーの場合、これは、各 HTTP リクエストが独自のスレッドによって処理されることを意味します。リレーショナル データベース サーバーの場合、これは、各 SQL トランザクションも独自のスレッドによって処理されることを意味します。Java EE サーバーを使用したことがある場合は、これがその仕組みであることを理解してください。したがって、リクエストごとのスレッドのスタイルは次のようになります: 1 つのリクエスト = 1 つのトランザクション = 1 つのスレッド

それで、このモデルの価格はいくらですか? このコストを理解するには、Java のスレッドのコストを理解する必要があります。プラットフォームのスレッドと CPU 使用率のコスト。

Java スレッドは、Java の以前のバージョンでプラットフォーム スレッドとして作成され、オペレーティング システム スレッドのシン ラッパーとも呼ばれます。それらについて知っておくべきことが 2 つあります。

  • プラットフォーム スレッドはコール スタックをメモリに保存する必要がある
  • これはシステム リソースであり、プラットフォーム スレッドの開始に約 1 ミリ秒かかります。

実際、プラットフォーム スレッドはかなり高価なリソースです。このようなスレッドを使用してハードウェアの使用率を最適化するにはどうすればよいでしょうか?

アプリケーションに 16 GB の使用可能なメモリがあると仮定します。20 MB のスレッド サイズで割ると、このようなマシンには 800 スレッドの余地があります。これらのスレッドがネットワーク上のリソースへのアクセスなど、何らかの I/O を実行しているとします。リソースが 100 ミリ秒以内にアクセスされたと仮定します。リクエストの準備とレスポンスの処理は 10 ナノ秒以内に完了します。これらすべてのメモリ内計算には 1000 ナノ秒かかると仮定します。これは、リクエストの準備とレスポンスの処理の間には約 100,000 の係数があり、レスポンスを取得するまでにかかる時間があることを意味します。その間、スレッドは何もせずにただそこに座っているだけです。したがって、これらのスレッドが 800 個ある場合、CPU 使用率はわずか 0.8% にすぎません。

メモリを 2 倍の 32 GB にすると、CPU 使用率は 1.3% に達する可能性がありますが、これはまだ低いです。

90% の CPU 使用率を達成したい場合は、逆に考えてみましょう。これには 90,000 のスレッドが必要となり、開始までに 90 秒かかり、1.8 TB のメモリを消費します。

プラットフォーム スレッドのコストが高すぎて、最適に近いハードウェア使用率で拡張できないことは明らかです。したがって、このような問題を解決するには別のスレッド モデルが必要です。

  1. クラシック Java スレッドに基づく既存のコードで、最小限の変更で仮想スレッドを使用できるようにします。

この目標は、従来のスレッドによって実行されるすべての処理を仮想スレッド処理メソッドに簡単に変換できることを意味します。ここではいくつかの重要なポイントについて説明します。

  • 仮想スレッドは、任意の Java コードまたは任意のネイティブ コードを実行できます。
  • 新しい概念を学ぶ必要はありません。
  • ただし、次のような特定の考えを捨て去る必要があります。
    • 仮想スレッドは安価で、従来のプラットフォーム スレッドよりも約 1,000 倍安価です。
    • 仮想スレッドをブロックするコストも非常に低いため、仮想スレッドのブロックを回避しようとしても無駄です。
    • 従来のブロッキング コードを作成しても問題ありません。ブロッキング コードは非同期コードよりも簡単に作成できるため、これは良いことです。この時点で、仮想スレッドをプールするのは良い考えなのでしょうか?と疑問に思われるかもしれません。答えはノーです。そんなことはしないでください。時間を無駄にしているだけです。

仮想スレッドに関する良いニュースがさらに 2 つあります。スレッド ローカル変数も同様に機能し、同期も機能します。同期についてはいくつか注意すべき点があります。仮想スレッドは引き続きプラットフォーム スレッドの上で実行され、その下にプラットフォーム スレッドがあります。ただし、この仮想スレッドはプラットフォーム スレッドから切り離して、プラットフォーム スレッドが別の仮想スレッドを実行できるようにすることができます。いつ出発できますか? 仮想スレッドは、ブロックされるとプラットフォーム スレッドから切り離すことができます。I/O 操作または同期操作でブロックされるか、スリープ状態になる可能性があります。仮想スレッドが同期ブロック内で何らかのコードを実行している場合、そのプラットフォーム スレッドから切り離すことはできません。したがって、この同期されたコード ブロックの実行中にプラットフォーム スレッドがブロックされます。この時間が短ければ大丈夫です。パニックに陥ったり、これを防ぐために何らかの措置を講じたりする必要はありません。これに長い時間がかかる場合、つまり、長時間の I/O 操作を実行する場合、状況はあまり良くありません。synchronizedへの呼び出しを再入可能なロックに置き換えるだけで、この問題の発生を防ぐことができます。

コーディングをさらに深く掘り下げる

仮想スレッドの作成方法については、以前のJava 21 新機能 仮想スレッドで述べました。Thread.ofVirtual()たとえば次のようにを渡すだけです。

Thread.ofVirtual()
        .name("didispace-virtual-thread")
        .start(runnable);

ヒント: プラットフォーム スレッドを作成したい場合は、以下を使用できます。Thread.ofPlatform()

仮想スレッドはプラットフォーム スレッド上で動作します。パフォーマンスの向上はなく、オーバーヘッドが発生するだけだと考えるかもしれません。どうしたの?仮想スレッドについてはさらに説明すべきことがあります。このコードがどのように機能するかを見てみましょう。

このコードでは、ストリーミング モードを使用して 10 個の未開始の仮想スレッドを作成します。これらのスレッドが実行しているタスクは、現在のスレッドを出力するだけです。次に、10 ミリ秒間スリープさせてから、スレッドの名前を再度出力します。最後に、これらの未開始のスレッドを開始し、join メソッドを呼び出して、すべてがコンソールに表示されることを確認します。

このコードを実行すると、ここで本当に予期せぬことが起こっていることがわかります。

この ForkJoinPool のスレッド 7 がスリープから復帰すると、元のプラットフォーム スレッドで実行を継続せず、別のプラットフォーム スレッドにジャンプします。これを自分のマシンで実行している場合は、1 つまたは 2 つのスレッドだけではこれが観察されない可能性があるため、十分な数の仮想スレッドを開始するようにしてください。

舞台裏でどのように機能するか

実際、何らかの操作により仮想スレッドがブロックされると、対応するスタックが、仮想スレッドが実行されているプラ​​ットフォーム スレッドからヒープ メモリに移動されます。したがって、このプラットフォーム スレッドは別の仮想スレッドを自由に実行できるようになりました。タスクが実行を継続できるという信号を受け取ると、そのスタックはヒープからプラットフォーム スレッドに戻されますが、必ずしも同じである必要はありません。つまり、これは仮想スレッドをブロックし、その仮想スレッドのスタックをメイン メモリに移動したり、元のメモリに戻したりするコストになります。仮想スレッドのブロックは無料ではありませんが、プラットフォーム スレッドをブロックするよりもはるかにコスト効率が高くなります。

ヒント: このロジックビデオには図による説明がありますが、アニメーションビデオと合わせて見ると理解しやすくなります。

幸いなことに、JDK のブロッキング操作はすべて、JDK を利用できるようにリファクタリングされています。これには、I/O 操作、同期、および が含まれますThread.sleep

仮想スレッドを実行するために必要なプラットフォーム スレッドの数

この問題に関しては、テストすることができます。仮想スレッドを作成し、対応するプラットフォーム スレッド名をすべて収集しましょう。

このコードは基本的に 5 つの仮想スレッドを開始し、その後、いくつかのコードを使用してプール名とプラットフォーム スレッド名を抽出します。最後に、さまざまな統計、このコードの実行にかかった時間、CPU 上のコアの数、スレッド プールの数、プラットフォーム スレッドの数を出力するだけです。

このコードを実行すると、次の結果が表示されます。

5 つの仮想スレッドの場合、3 つのプラットフォーム スレッドが使用され、2 ミリ秒かかります。

10 個の仮想スレッドを使用して、コードを再度実行してみましょう。

10 スレッドでも 3 つのプラットフォーム スレッドが使用され、4 ミリ秒かかりました。

100 個の仮想スレッドを使用して、コードを再度実行してみましょう。

現在、7 つのプラットフォーム スレッドが使用されています。

1,000 個の仮想スレッドで何が起こるかを見てみましょう。

依然として 7 つのプラットフォーム スレッドを使用します。

100,000 の仮想スレッドを試してみてはいかがでしょうか?

現在は 8 つのプラットフォーム スレッドを使用し、156 ミリ秒かかります。

ちなみに、これらのスレッドが大したことを行っておらず、文字列操作と同時セットへの要素の追加だけを行っている場合でも、これらのスレッドすべてを実行するのに 156 ミリ秒しかかからないことがわかります。

これを 100 万スレッドに増やしてみましょう。

所要時間は 1 秒もかかりませんでしたが、それでも 8 つのプラットフォーム スレッドが使用されました。

1,000 万の仮想スレッドを開始する

1000 万の仮想スレッドを立ち上げてみてはどうでしょうか? これをやってみたことがありますか?あなたのマシン上で 1,000 万個のプラットフォーム スレッドを起動しますか? まあ、通常これは不可能ですが、仮想スレッドを使用するとそれができるかもしれません。次の結果が得られます。

これは古いラップトップでテストしたところ、7 秒もかかりませんでした。これは非常に素晴らしいことです。

これが Java の仮想スレッドです。すごくないですか?では、すでに Java 21 へのアップグレードを開始し、アプリケーションのパフォーマンスを向上させるためにこの機能を使用し始めていますか? メッセージエリアで一緒にチャットしましょう。

コーディングは簡単ではありません。この記事が気に入っていただけましたら、いいね、読んで、転送してサポートしてください。勉強中に困難に遭遇したらどうしますか?当社の超質の高い技術交流グループに参加して、より良い学習と進歩を図るための交流やディスカッションに参加できます。あと、行かないでついて来てね!Java の新機能コラムを継続的に更新します

おすすめ

転載: blog.csdn.net/dyc87112/article/details/134044448