OpenJDKプロジェクトルーム

  • link:/category/architecture/ [アーキテクチャ]

  • Java

  • JVM

[[project loom]]
=== 1. 概要

この記事では、https://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html [Project Loom]を簡単に見ていきます。 本質的に、* Project Loomの主な目標は、Javaで高スループットの軽量同時実行モデルをサポートすることです*。

[[project loom]]
=== 2. プロジェクトルーム

Project Loomは、OpenJDKコミュニティによる軽量の並行性構造をJavaに導入する試みです。 Loomのプロトタイプは、これまでJVMとJavaライブラリに変更を加えました。
Loomの予定されたリリースはまだありませんが、recenthttps://wiki.openjdk.java.net/display/loom/Main [Project Loom's wiki]で最近のプロトタイプにアクセスできます。
Loomのさまざまな概念について説明する前に、Javaの現在の並行性モデルについて説明しましょう。

[[java concurrency]]
=== 3. Java同時実行モデル

現在、_Thread_は、Javaの並行性のコア抽象化を表しています。 この抽象化は、他のlink:/java-util-concurrent[concurrent APIs]とともに、並行アプリケーションの作成を容易にします。
ただし、Javaは実装にOSカーネルスレッドを使用するため、今日の並行性の要件を満たすことができません。 特に2つの大きな問題があります。
  1. Threads は、ドメインの単位のスケールと一致しません
    並行性 たとえば、通常、アプリケーションは最大数百万のトランザクション、ユーザー、またはセッションを許可します。 ただし、カーネルでサポートされるスレッドの数ははるかに少なくなります。 したがって、すべてのユーザー、トランザクション、またはセッションの* _Thread_は、多くの場合実行不可能です。*

  2. ほとんどの並行アプリケーションでは、スレッド間で何らかの同期が必要です
    すべてのリクエストに対して。 このため、* OSスレッド間で高価なコンテキストスイッチが発生します。*

    このような問題の可能な解決策は、*非同期同時APIの使用*です。 一般的な例は、_https://www.baeldung.com/java-completablefuture [CompletableFuture] _およびlink:/rx-java[RxJava]です。 このようなAPIがカーネルスレッドをブロックしない場合、アプリケーションはJavaスレッドの上にきめ細かな同時実行構造を提供します__.__
    一方、*このようなAPIは、デバッグやレガシーAPIとの統合が困難です*。 したがって、カーネルスレッドに依存しない軽量の同時実行性構造が必要です。

[[task scheduler]]
=== 4. タスクとスケジューラ

スレッドの実装は、軽量でも重量でも、2つの構成要素に依存します。
  1. タスク(継続とも呼ばれます)–一連の指示
    ブロッキング操作のために自身を一時停止できる

  2. スケジューラ– CPUに継続を割り当て、再割り当てするため
    一時停止した継続からのCPU

    現在、* Javaは継続とスケジューラの両方についてOS実装に依存しています*。
    現在、継続を中断するには、コールスタック全体を保存する必要があります。 同様に、再開時にコールスタックを取得します。 *継続のOS実装にはネイティブコールスタックとJavaのコールスタックが含まれるため、フットプリントが大きくなります*。
    しかし、より大きな問題は、OSスケジューラの使用です。 スケジューラはカーネルモードで実行されるため、スレッド間の区別はありません。 そして、すべてのCPU要求を同じ方法で処理します。
    このタイプのスケジューリングは、*特にJavaアプリケーションには最適ではありません*。
    たとえば、要求に対して何らかのアクションを実行し、その後の処理のためにデータを別のスレッドに渡すアプリケーションスレッドを考えます。 ここでは、*これらのスレッドを同じCPUでスケジュールする方が良いでしょう*。 しかし、スケジューラはCPUを要求するスレッドにとらわれないため、これを保証することは不可能です。
    Project Loomは、OSの実装ではなく、継続とスケジューラのJavaランタイム実装に依存する**ユーザーモードスレッドを通じてこれを解決することを提案します** __.__

[[loom fiber]]
=== 5. 繊維

OpenJDKの最近のプロトタイプでは、_Thread_クラスとともに_Fiber_という名前の新しいクラスがライブラリに導入されています。
_Fibers_の計画されたライブラリは_Thread_に類似しているため、ユーザー実装も同様のままである必要があります。 ただし、主に2つの違いがあります。
  1. Fiber would タスクを内部ユーザーモードの継続でラップします。
    これにより、カーネル*ではなくJavaランタイムでタスクを一時停止および再開できます

  2. プラガブルユーザーモードスケジューラ(_ForkJoinPool、_など)は次のようになります
    used

    これら2つの項目を詳しく見ていきましょう。

6. 続き

継続(またはコルーチン)は、後の段階で呼び出し元によって生成および再開できる一連の命令です。
すべての継続には、エントリポイントと降伏ポイントがあります。 降伏点は、中断された場所です。 呼び出し元が継続を再開するたびに、コントロールは最後の降伏点に戻ります。
*このサスペンド/レジュームは、OS *ではなく言語ランタイムで発生することを理解することが重要です*。 したがって、カーネルスレッド間の高価なコンテキストの切り替えを防ぎます。
スレッドと同様に、Project Loomはネストされたファイバーのサポートを目指しています。 ファイバーは内部的に継続に依存しているため、ネストされた継続もサポートする必要があります。 これをよりよく理解するには、ネストを許可するクラス__Continuation __を検討してください。
Continuation cont1 = new Continuation(() -> {
    Continuation cont2 = new Continuation(() -> {
        //do something
        suspend(SCOPE_CONT_2);
        suspend(SCOPE_CONT_1);
    });
});
上記のように、ネストされた継続は、スコープvariable__を渡すことで、それ自体またはそれを含む継続を中断できます。 __このため、*それらは_scoped_継続として知られています*。
継続を中断するには呼び出しスタックを保存する必要もあるため、プロジェクトLoomの目標は、継続を再開しながら軽量のスタック取得を追加することでもあります。

[[forkjoin scheduler]]
=== 7. スケジューラー

前に、同じCPUで関連するスレッドをスケジュールする際のOSスケジューラーの欠点について説明しました。
Project Loomがファイバーを使用したプラガブルスケジューラを許可することは目標ですが、非同期モードのin * https://www.baeldung.com/java-fork-join [_ForkJoinPool_]がデフォルトのスケジューラとして使用されます。 *
__workkteainPool __works * work-stealing algorithm *。 したがって、すべてのスレッドはタスクの両端キューを保持し、先頭からタスクを実行します。 さらに、アイドル状態のスレッドはブロックせず、タスクを待機して、代わりに別のスレッドの両端キューの末尾からプルします。 *
非同期モードの唯一の違いは、ワーカースレッドが別のdequeの先頭からタスクを盗むことです*。
__ForkJoinPool __ **は、実行中の別のタスクによってスケジュールされたタスクをローカルキューに追加します。 したがって、同じCPUで実行します。 **

8. 結論

この記事では、Javaの現在の並行性モデルの問題とhttps://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html[Project Loom]によって提案された変更について説明しました。
その際、タスクとスケジューラも定義し、* FibersとForkJoinPoolがカーネルスレッドを使用してJavaに代わる方法を提供する方法を調べました。*