6 スレッド (Alibaba Healthy Two Faces)
6.1 スレッド プール内のスレッドの数とテーブルの数の関係は何ですか?
回答: 当時私が考えたのは、コアスレッド数を 20 に設定することでした。これは、計算式 (コアスレッド数 = CPU コア数 (スレッド待ち時間 / スレッド実行タスクの平均時間 + 1)) に基づいています。スレッドの最大数はテーブルに設定されており、ブロッキング キューを使用します。データ同期には大量のデータ移行タスクが含まれるため、ここでの非コア スレッドの数はあえて大きく設定しません。同期プロセス中に同時に mysql から取得されるデータが多すぎると、OOM が発生する可能性があります。
基本的に同期対象のテーブルデータ量が少なく、その後の変更操作を行う場合には、同期操作にはcachedThreadPoolを使用することをお勧めします。
6.2 IO 集中型の要件である場合、CPU アイドル時間は非常に長くなります。スレッド プールをより適切に設計するにはどうすればよいですか?
これは IO 集中型の要件であるため、タスクの IO 時間が非常に長くなります。つまり、ネットワーク IO 中にタスクがブロックされ、CPU アイドル時間が非常に長くなります。
IO 集中型のタスクの場合、スレッドは IO (ディスク操作やネットワーク要求など) を待機している間に CPU を消費しないため、より多くのスレッドを使用してシステムのスループットを向上させることができます。ただし、スレッド自体はメモリ (スレッド スタックなど) を必要とし、スレッド スケジューリングのオーバーヘッドが追加されるため、解放されません。したがって、スレッドの数が多すぎることはできません。
IO 集中型のニーズに合わせてスレッド プールを設計するためのいくつかの提案を次に示します。
-
スレッド番号設定:
- スレッド数を比較的大きく設定することを検討できます。一般的な戦略は
CPU核数 * 2
1 以上です。 - システムの実際の IO 待機時間と CPU 時間の比率に基づいて、スレッドの数を動的に調整することも検討できます。
- スレッド数を比較的大きく設定することを検討できます。一般的な戦略は
-
適切なキューを選択します。
- たとえば、境界付きキューを使用すると、
ArrayBlockingQueue
タスクがバックログされてメモリ オーバーフローが発生するのを防ぐことができます。 - 実際のビジネス負荷に基づいてキュー サイズを合理的に選択します。キューが大きすぎるとより多くのメモリを消費する可能性があり、キューが小さすぎると拒否されるタスクが多すぎる可能性があります。
- たとえば、境界付きキューを使用すると、
-
スレッド生存時間:
- IO 集中型のタスクの場合、スレッド生存時間を比較的短く設定できるため、不要になったスレッド リソースをより早く解放できます。
-
拒否ポリシー:
- キューがいっぱいでスレッド数が最大に達した場合、受信したタスクを処理するための戦略が必要になります。デフォルトの戦略は throw です
RejectedExecutionException
が、呼び出し元実行戦略などの他の戦略を選択して、タスクを送信したスレッドがタスク自体を実行できるようにすることもできます。
- キューがいっぱいでスレッド数が最大に達した場合、受信したタスクを処理するための戦略が必要になります。デフォルトの戦略は throw です
-
タスクの優先順位を使用します。
- 一部の IO タスクが他のタスクよりも緊急である場合は、優先キューの使用を検討してください。
-
モニタリングとチューニング:
- キューの長さ、スレッド数、CPU 使用率など、スレッド プールの主要な指標を監視します。この情報は、スレッド プール構成を調整する必要があるかどうかを判断するために使用できます。
- ビジネスのピーク時間帯に応じてスレッド プールの構成を調整し、たとえば夜間の IO 需要が低い場合は、スレッド数を減らすことができます。
要約すると、IO 集中型の要件の場合、主な目標はシステムのスループットを最大化することです。これは通常、より多くのスレッドを使用し、スレッド プールのパラメーターを適切に構成することを意味します。
6.3 スレッド プール内のスレッドはいつ作成されたと思いますか?
回答: スレッド プールの宣言時にコア スレッドを作成できます。これはスレッドの予熱に相当します。タスクは到着するとすぐに使用できます。これは preStart パラメータによって制御されます。
6.4 スレッドのステータスは何だと思いますか?
答え: 実行中、準備完了、ブロック済み、終了済み
Java では、スレッドのライフサイクルには複数の状態が含まれます。Java スレッドの主な状態は次のとおりです。
-
新しい (新しい) :
- スレッド オブジェクトを作成したがまだその
start()
メソッドを呼び出していないとき、スレッドはこの状態になります。
- スレッド オブジェクトを作成したがまだその
-
実行可能 (RUNNABLE) :
- スレッド オブジェクトは、そのメソッドが呼び出された
start()
ときに実行可能な状態になりますが、スレッド スケジューラはまだそのオブジェクトを現在の実行のスレッドとして選択していません。これには、Java 仮想マシン内で実行されているスレッドのステータスも含まれます。
- スレッド オブジェクトは、そのメソッドが呼び出された
-
ブロックされました:
- スレッドはモニター ロックを待機しています (たとえば、スレッドが同期メソッドまたは同期ブロックを呼び出し、現在の同期メソッドまたは同期ブロックがすでに別のスレッドによって保持されている場合、スレッドは BLOCKED 状態になります)。
-
待機中:
- 次のいずれかのメソッドが呼び出されたため、スレッドは待機状態になっています。
Object.wait()
Thread.join()
LockSupport.park()
- これは無制限の待機であり、スレッドは他のスレッドによって明示的に起動される必要があります。
- 次のいずれかのメソッドが呼び出されたため、スレッドは待機状態になっています。
-
待機中のタイムアウト (TIMED_WAITING) :
- スレッドが次のいずれかのメソッドを呼び出してタイムアウト パラメーターを指定すると、スレッドはこの状態になります。
Thread.sleep(long millis)
Object.wait(long timeout)
Thread.join(long millis)
LockSupport.parkNanos()
またはLockSupport.parkUntil()
- スレッドは、タイムアウトに達するか、別のスレッドによってウェイクアップされるまで、この状態のままになります。
- スレッドが次のいずれかのメソッドを呼び出してタイムアウト パラメーターを指定すると、スレッドはこの状態になります。
-
終了しました:
- スレッドは、メソッド
run()
が完了するか、スレッドが中断されると、この状態になります。
- スレッドは、メソッド
Java プログラミングでは、Thread.getState()
メソッドを使用してスレッドの現在のステータスを取得できます。これは、デバッグやスレッド管理に非常に役立ちます。
6.5 CPU 使用率が非常に高いことがわかった場合、スレッドのどの状態に注目しますか?
回答: スレッドがブロッキング状態にある場合、そのスレッドは CPU をあまり占有せず、ブロッキング キュー内で一時停止されるため、実行状態にあるスレッドに注目する必要があります。
6.6 アプリケーションのスレッド数がゆっくりと増加しており、現時点ではアクセス数もあまり変動しておらず、CPU と負荷もあまり変動していないことがわかった場合、それをどのように分析し、何をすべきか? このように分析するには、スレッドのどのような状態に注目しますか? (間違って答えてしまいました)
答え:
回答: アプリケーションのスレッド数がゆっくりと増加し続けているにもかかわらず、アクセス数、CPU、負荷が比較的安定している場合は、次のような状況が考えられます。
-
スレッドのリーク:一部のスレッドが適切に閉じられていない可能性があり、時間の経過とともにスレッドの数が徐々に増加します。これは、カスタム スレッドまたはスレッド プールを使用しているときに発生する可能性があります。
-
サードパーティのライブラリ/コンポーネント: 内部でスレッドを作成し、それらを正しく管理しないライブラリまたはコンポーネントを使用している可能性があります。
さらに分析するには:
-
スレッド スタック分析: Java の組み込みツールを使用して、
jstack
アプリケーションのスレッド スタックを表示できます。これにより、各スレッドにスレッドの動作を示すスナップショットが作成されます。このツールを使用すると、成長中のスレッドが何をしているのか、コードのどの部分が開始されているのかを特定できます。 -
ステータス、特にWAITING スレッドとTIMED_WAITINGスレッドに注意してください。これらのスレッドは CPU を大量に消費するわけではありませんが、特定のリソースまたは特定の条件を待っているためにブロックされ、スレッド数が増加する可能性があります。
-
監視ツール: VisualVM、JProfiler などのツールを使用して、スレッドの作成、ステータス、破棄をリアルタイムで監視します。これは、スレッド作成のパターンとスレッド リークの可能性を特定するのに役立ちます。
-
ログの確認: アプリケーションのログを確認して、スレッドの動作に関連する可能性のある例外、エラー、またはその他の関連情報を確認します。
-
コード レビュー: スレッドまたはスレッド プールが作成されるコード内のすべての場所をチェックし、スレッドがタスクの完了後に適切にシャットダウンされていることを確認します。
結論: スレッド数が増加し続けているにもかかわらず、他の指標が比較的安定している場合は、スレッド管理の問題またはスレッド リークである可能性があります。重要なのは、どのスレッドが作成され続けているか、そしてそれらが適切にシャットダウンされない理由を見つけることです。
6.7 フレーム グラフの水平方向と垂直方向をどのように理解しますか? 先ほど述べた問題は解決できますか?
回答: 水平方向には、スレッドの実行時に費やされる CPU 時間を表し、垂直方向には、メソッドの呼び出しスタックを表します。なぜなら、スレッドが CPU をあまり占有しないということですから、タスクは実行されるが正しく解放されないため、フレーム グラフは適用できません。この問題を解決するには