[マルチスレッド] —— スレッドプール

1. スレッド プールとは何か、スレッド プール (作成) とは何ですか

スレッドプールとは、あらかじめ複数のスレッドオブジェクトをコンテナに入れておくもので、これを利用する場合、新たにスレッドを使用する必要はなく、プールに直接アクセスしてスレッドを取得するだけで、サブスレッドを作成したり、スレッドを作成したりする手間が省けます
。コードの実行効率が向上します。

さまざまなスレッド プールを生成するための静的メソッドは、JDK の java.uti.concurrent./executors クラスで提供されます。

ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(4);
ScheduledExecutorService newScheduledThreadPool =
Executors.newScheduledThreadPool(4);
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();

次に、execute メソッドを呼び出します。
これら 4 つのスレッド プールの最下層はすべて ThreadPoolExecutor オブジェクトによって実装されており、Ali 仕様マニュアルではスレッド プールは
ThreadPoolExecutor によってカスタマイズされると規定されており、実際の開発も同様です。

(1) 新しいキャッシュされたスレッドプール

キャッシュ可能なスレッド プールを作成します。スレッド プールの長さが処理の必要性を超えた場合、アイドル状態のスレッドを柔軟にリサイクルできます。
リサイクルがない場合は、新しいスレッドが作成されます。このタイプのスレッド プールの特徴は、
作成されるワーカー スレッドの数にほとんど制限がないため (実際には制限があり、数は Integer.
MAX_VALUE です)、スレッド プールにスレッドを柔軟に追加できることです。 。
スレッド プールにタスクが長期間送信されなかった場合、つまり、ワーカー スレッドが指定された時間 (
デフォルトでは 1 分) アイドル状態になった場合、ワーカー スレッドは自動的に終了します。終了後に新しいタスクを送信すると、スレッド
プールはワーカー スレッドを再作成します。CachedThreadPool を使用する場合は、タスク数の制御に注意する必要があります。そうしないと、同時に
多数のスレッドが実行され、システム麻痺が発生する可能性があります。

(2) 新しい固定スレッドプール

指定された数のワーカー スレッドを含むスレッド プールを作成します。タスクが送信されるたびにワーカー スレッドが作成され、
ワー​​カー スレッドの数がスレッド プールの初期最大数に達すると、送信されたタスクはプール キューに格納されます。
FixedThreadPool は典型的な優れたスレッド プールであり、
プログラムの効率を向上させ、スレッド作成のオーバーヘッドを節約するという利点があります。ただし、スレッド プールがアイドル状態のとき、つまり、スレッド プールに実行可能なタスクがないときは
、ワーカー スレッドは解放されず、特定のシステム リソースも占有します。

(3) newSingleThreadExecutor

シングルスレッド Executor を作成します。つまり、タスクを実行するための一意のワーカー スレッドのみを作成し、タスクの実行に
唯一のワーカー スレッドのみを使用して、すべてのタスクが指定された順序 (FIFO、LIFO、優先順位) で実行されるようにします。
このスレッドが異常終了すると、別のスレッドが代わりに実行され、順次実行が保証されます。シングル ワーカー スレッドの最大の特徴は、タスクが順番に実行され、
同時に複数のスレッドが
アクティブにならないことが保証されることです。

(4) 新しいスケジュールスレッドプール

固定長のスレッド プールを作成し、タイミングと定期的なタスクの実行をサポートします。たとえば、実行を 3 秒遅らせます

2. スレッド プールを使用する理由は何ですか?

(1) スレッドプールの仕事は主に実行中のスレッド数を制御することであり、処理中にタスクがキューに入れられ、
スレッドが作成された後にこれらのタスクが開始されます。番号に応じて、余剰のスレッドが line で待機します
。他のスレッドが実行を完了するのを待ってから、タスクをキューから取り出して実行します。

(2) 主な機能: スレッドの再利用、最大同時実行数の制御: スレッドの管理。
まず、リソース消費を削減します。作成されたスレッドを再利用することで、スレッドの作成と破棄のコストを削減します

2つ目:応答速度の向上。タスクが到着すると、スレッドの作成を待たずにすぐにタスクを実行できます

3 番目: スレッドの管理性を向上させます。スレッドは希少なリソースです。無制限に作成すると、
システムリソースを消費するだけでなく、システムの安定性も低下します。スレッドプールを使用すると、一元的な割り当て、チューニング、監視が可能になります。

3. スレッド プールの基本的な動作原理

ここに画像の説明を挿入

  • (1) ステップ 1: スレッド プールが最初に作成されたとき、その中にスレッドは存在せず、タスクが来るまでスレッドは作成されません
    もちろん、 prestartAllCoreThreads() または
    prestartCoreThread() メソッドを呼び出して corePoolSize スレッドを事前に作成することもできます。
  • (2) ステップ 2:execute() を呼び出してタスクを送信するときに、現在のワーカー スレッド数
    < corePoolSize の場合、タスクを実行するための新しいスレッドを直接作成します。
  • (3) ステップ 3: その時点でワーカー スレッド数 >= corePoolSize の場合、タスクはタスク キューにキャッシュされます
  • (4) ステップ 4: キューがいっぱいで、スレッド プール内のワーカー スレッドの数が maxPoolSize
    未満の場合でも、このタスクを実行するためのスレッドが作成されます。
  • (5) ステップ 5: キューがいっぱいで、スレッド プール内のスレッドが最大プールサイズに達した場合、
    この時点で拒否ポリシーが実行されます。JAVA スレッド プールのデフォルト ポリシーは AbortPolicy です。つまり、
    RejectedExecutionExceptionが発生します。投げられる

4. ThreadPoolExecutor オブジェクトにはどのようなパラメータがありますか? コアスレッドの数と最大スレッド数を設定するにはどうすればよいですか? 拒否戦略とは何ですか?

まず、パラメータが 7 つあることは誰もが知っています。

1) corePoolSize: コアスレッドの数、

ThreadPoolExecutor には、これに関連する設定があります。allowCoreThreadTimeOut (デフォルト
は false)、allowCoreThreadTimeOut が false の場合、コア スレッドは常に
アイドル状態であっても常に存続します。
また、allowCoreThreadTimeOut が true の場合、アイドル時間が keepAliveTime を超えるとコア スレッドがリサイクルされます。

(2)maximumPoolSize: 最大スレッド数

スレッド プールが収容できるスレッドの最大数。スレッド プール内のスレッド数が最大値に達すると、
この時点でタスクの追加に拒否戦略が採用されます。デフォルトの拒否戦略は、ランタイム エラー
(RejectedExecutionException)をスローすることです。 )。初期化に使用されるワークキューが
LinkedBlockingDeque である場合、この値は無効になることに注意してください。

(3) keepAliveTime: 生存時間、

非コア アイドル状態がこの時間を超えるとリサイクルされ、アイドル状態のコア スレッドがリサイクルされるかどうかは、
allowCoreThreadTimeOut の影響を受けます。

(4)単位:keepAliveTimeの単位。

(5) workQueue: タスクキュー

一般的に使用されるキューには、SynchronousQueue、LinkedBlockingDeque (無制限のキュー
)、および ArrayBlockingQueue (制限付きキュー) の 3 つがあります。

(6) threadFactory: スレッドファクトリ、

ThreadFactory はワーカーを作成するために使用されるインターフェイスです。スレッドの一部のプロパティは、
スレッド ファクトリを通じてカスタマイズできます。デフォルトでは、新しいスレッドが直接作成されます。

(7) RejectedExecutionHandler: 拒否戦略

また、メソッドが 1 つだけのインターフェイスでもあり、スレッド プール内のリソースがすべて使用され、新しいスレッドの追加が拒否された場合、
RejectedExecutionHandler の RejectedExecution メソッドが呼び出されます。デフォルトでは、
実行時例外がスローされます。

スレッドプールサイズの設定

スレッド プール サイズの設定:

  1. スレッド プールによって実行されるタスクの特性 (CPU 集中型または IO 集中型) を分析する必要がある
  2. 各タスクの平均実行時間はどれくらいですか? このタスクの実行時間は、
    タスク処理ロジックにネットワーク送信や基礎となるシステム リソースの依存関係が含まれるかどうかにも関係する可能性があります。

主に計算タスクの実行で CPU を大量に使用するタスクの場合、応答時間が速く、CPU が常に実行されます。この種のタスクは CPU の使用率が高いため、スレッド数の構成を適切にする必要があります
。 CPU コアの数に応じて決定され、
CPU コアの数 = 最大同時実行スレッド数。CPU コアの数に 4 を追加すると、サーバーは最大
4 つのスレッドを同時に実行できます。スレッドが多すぎるとコンテキストの切り替えが発生し、効率が低下します。スレッド プール内のスレッドの最大数は、
CPU コアの数 + 1 として構成できます。

IO 集中型の場合、主に IO 操作が実行され、
CPU がアイドル状態になり、CPU 使用率が低下するため、IO 操作の実行に時間がかかります。この場合、スレッドの
サイズはプールを増やすことができます。この場合、スレッドの待ち時間を組み合わせて判断することができ、待ち時間が長いほど
スレッドの数が多いことになります。一般に、CPU コアの数を 2 倍構成できます。

計算式: スレッド プールに設定された最適なスレッド数 = ((スレッド プールに設定されたスレッド待ち時間 + スレッド
CPU 時間) /
スレッド CPU 時間) * CPU 数
この計算式のスレッド CPU 時間は、プログラムのシングル スレッドの推定値ですCPU 内での実行時間 (通常は、
loadrunner を使用して多数の実行をテストし、平均を見つけます)

拒否ポリシー

  1. AbortPolicy: 例外を直接スローします。デフォルトのポリシーです。
  2. CallerRunsPolicy: 呼び出し元のスレッドを使用してタスクを実行します。
  3. DiscardOldestPolicy: ブロッキング キュー内の先頭のタスクを破棄し、現在のタスクを実行します。
  4. DiscardPolicy: タスクを直接破棄します。もちろん、
    アプリケーション シナリオに従って RejectedExecutionHandler インターフェイスを実装し、
    処理できないタスクのログ記録や永続的な保存などの飽和ポリシーをカスタマイズすることもできます。

5. 一般的なスレッドセーフな同時コンテナは何ですか?

CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap
CopyOnWriteArrayList、CopyOnWriteArraySet は、コピーオンライトを使用してスレッド セーフを実現します
ConcurrentHashMap は、セグメント ロックを使用してスレッド セーフを実現します


次の記事も参照してください。
1. Java スレッド プールの 7 つのパラメータの詳細な説明

2.スレッドプール

おすすめ

転載: blog.csdn.net/qq_36256590/article/details/132382788