スレッドプールThreadPoolExecutor
9263 ワード
本文
スレッドプールを使用する理由
スレッドプールは、非同期または同時実行が必要なほとんどのプログラムで使用できます.合理的な使用は私たちに以下のメリットをもたらします.•システム消費量の削減:作成したスレッドを再利用して、スレッドの作成と破棄によるリソース消費量を削減します.•応答速度の向上:タスクが到着した場合、スレッドが作成されるまでタスクを実行する必要はありません.•スレッドの管理を可能にする:合理的な割り当て、チューニング、監視を設定することができる.
スレッドプールワークフロー1、コアスレッドプール内のスレッドがすべてタスクを実行しているかどうかを判断します.いいえ.新しいワークスレッドを作成してタスクを実行します.はい、次の流れに進みます.2、ワークキューがいっぱいかどうかを判断します.いいえ.新しいタスクはこのワークキューに格納されます.はい.次のプロセスに進みます.3、スレッドプール内のスレッドがすべて動作しているかどうかを判断します.いいえ--」新しいワークスレッドを作成してタスクを実行します.はい--」次のプロセスに進みます.4、設定されたポリシーに従って実行できないタスクを処理する.
スレッドプール構築関数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
1. CorePoolSize:コアスレッドプールサイズです.タスクをコミットすると、スレッドプールはタスクを実行するためにスレッドを作成します.他の空きコアスレッドが新しいタスクを実行できる場合でも作成されます.実行を待つタスク数がスレッドコアサイズより大きい場合は作成されません.作成したばかりのスレッドプールで、まだタスクがない場合は、コアスレッドを作成します.十分なコアスレッドを作成するためです.後で私のコアスレッドはずっと維持されています.
2. maximumPoolSize:スレッドプールの最大数、作成可能な最大スレッド数、キューがいっぱいで、作成したスレッド数が最大スレッド数より小さい場合、新しいスレッド実行タスクが作成されます.無境界キューであれば、いつまでも満たされず、このパラメータは役に立たない.キューがいっぱいになると、コアスレッドはまだ現在のタスクのニーズを満たすことができないと考えられ、複数のセカンダリスレッドを拡張して作業を手伝い、セカンダリスレッドが空いていて、大丈夫になったら、これらのセカンダリスレッドをキャンセルし、コアスレッドだけを保持します.
3. keepAliveTime:スレッドはアクティブな時間を保持し、スレッドプールの作業スレッドは空き後、生存時間を維持するため、タスクが多く、各タスクの実行時間が短い場合は、時間を大きくしてスレッドの利用率を高めることができます.
4. unit:スレッド保持活動時間単位、日(DAYS)、時間(HOURS)、分(MINUTES、ミリ秒MILLISECONDS)、マイクロ秒(MICROSECONDS)、ナノ秒(NANOSECONDS)
5.workQueue:タスクキュー実行待ちのタスクのブロックキューを保存します.一般的には、ArrayBlockingQueue:配列ベースの境界ブロックキューを選択できます.作成時にサイズを指定するLinkedBlockingQueue:チェーンテーブルベースのブロックキューを指定します.作成時にサイズを指定できます.指定しない場合intの最大値SynchronizedQueue:要素を格納しないブロックキュー.PriorityBlockingQueue:優先度のあるブロックキュー.
6.threadFactory:スレッドを作成するファクトリを設定し、作成したスレッドごとにより意味のある名前を設定できます.
7.handler:飽和ポリシーは拒否ポリシーとも呼ばれます.キューとスレッドプールがいっぱいになると飽和状態になります.新しいタスクを処理するためにポリシーを取る必要があります.デフォルトポリシーはAbortPolicyです.AbortPolicy:直接例外を投げ出します.CallerRunsPolicy:キューがいっぱいで、タスクを失わず、例外を投げ出さず、スレッドプールに追加できなかったら、では、メインスレッドは自分でそのタスクを実行します.DiscardOldestPolicy:キュー内の最も古いタスク(最も早くキューに入ったタスク)を削除します.を選択して、新しいタスクを再発行してみます.DiscardPolicy:処理せずにそのまま捨てます.もちろん、自分のアプリケーションシーンに合わせてRejectedExecutionHandlerインタフェースのカスタムポリシーを実現できます.
注意:スレッドプールにはコアスレッド、補助スレッドはありません.私は上の言い方で、そのいくつかのパラメータの役割をよりよく区別するためです.スレッドプールのすべてのスレッドは同等です.プールのスレッド数がコアスレッド数より大きい場合、空きスレッドをキャンセルして、プールのスレッド数をコア数に下げることができます.サイズの目的です.スレッドプールのスレッド数は最終的にコアスレッド数に維持されます.
スレッドプールへのタスクのコミットには、execute()とsubmit()の2つの方法でタスクをコミットできます.execute():戻り値がないため、タスクが正常に実行されたかどうかは判断できません.submit():戻り値が必要なタスクをコミットするために使用します.スレッドプールは、タスクが正常に実行されたかどうかを判断し、futureのget()で戻り値を取得できます.get()メソッドは、現在のスレッドがタスクの完了を知るのをブロックします.get(long timeout,TimeUnit unit)は、スーパーマーケット時間を設定できます.
スレッドプールを閉じるにはshutdown()またはshutdownNow()を使用します.スレッドプールを閉じます.スレッドプール内の作業スレッドを巡回し、スレッドのinterruptを1つずつ呼び出してスレッドを中断することが原理です.端末に応答できないタスクは永遠に停止できません.shutdownNowはまずスレッドプールの状態をSTOPに設定し、実行中または一時停止中のすべてのスレッドを停止し、実行待ちのスレッドに戻ります.リストを表示します.すべてのスレッドは、あなたが作業しているかどうかにかかわらず、すべて処理されます.shutdownは、スレッドプールのステータスをshutdownステータスに設定し、タスクを実行していないすべてのスレッドを中断します.空きスレッドが処理され、作業のスレッドが完了するのを待っています.両方を呼び出すと、isShutdownはtrueに戻ります.すべてのスレッドが完了すると、isShutdownはtrueに戻ります.タスクがすべて閉じられている場合、isTerminaedはtrueを返します.一般的にshutdownメソッドを呼び出してスレッドプールを閉じます.タスクが必ずしも完了しない場合は、shutdownNowメソッドを直接呼び出すことができます.
Executors
タスクプールThreadPoolExecutorはExecutorsによって迅速に作成でき、Executorsは主に5種類のタスクプールを生成することができます(まだ2種類は使用されていません)
SingleThreadExecutor:スレッドが1つしかないスレッドプール(固定スレッドが1つしかなく、タスクキューのデフォルト長はInteger.MAX_VALUEであり、スレッドは空きキャンセルされない)
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
FixedThreadPool:スレッド数を固定するスレッドプール(スレッド数が固定、タスクキュータスクキューのデフォルト長はInteger.MAX_VALUE、スレッドが空きキャンセルされない)
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
CachedThreadPool:キャッシュ可能なスレッドプール(タスクリストはキャッシュされず、タスクが来るとスレッドが開き、スレッドが60秒空いてキャンセルされ、スレッド数は最大Integer.MAX_VALUE)
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
ScheduledThreadPool:定期的なタスクを実行できる固定数のスレッドプール(最大スレッド数はInteger.MAX_VALUE、タスクキューDelayedWorkQueue初期16、自己増加可能、毎回50%増加、最大はInteger.MAX_VALUE)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
SingleThreadScheduledExecutor:周期的なタスクを実行できるスレッドプール(コアスレッドは1つ、最大スレッド数はInteger.MAX_VALUE、タスクキューDelayedWorkQueue初期16、自己増加可能、毎回50%増加、最大はInteger.MAX_VALUE)
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
注意:
SingleThreadExecutor、FixedThreadPool、CachedThreadPoolは新しいクラスではありません.彼らはみなThreadPoolExecutorのオブジェクトです.ただパラメータが異なるScheduledThreadPoolExecutorは新しいクラスで、ThreadPoolExecutorから継承されています.
多くの人がExecutorsを使用してスレッドプールを作成することに賛成していません.前の内容を見て分かるはずですが、Executorsが作成したスレッドプールは、タスクキューがInteger.MAX_VALUEか、最大スレッドがInteger.MAX_VALUEかで、明らかに実際のシステムでは不可能です.ほとんどの場合、タスクが多すぎてメモリがオーバーフローする可能性がありますので、私たちは廃棄を選択します.また、サーバのリソースが限られており、すべてのCPUをスレッドプールに使用することはできません.そのため、コアスレッド数と最大スレッド数を実際に制御する必要があります.
そのため、スレッドプールを使用するには、ThreadPoolExecutorのコンストラクション関数を自分で作成したほうがいいです.どうせそのいくつかのパラメータも複雑ではありません.