Javaスレッドプール2のExecutorsが作成した5つのスレッドプールと使用上の注意
7874 ワード
0 x 01 ThreadPoolExecutorについて
前のブログで述べたように、Executorsはツールクラスです.スレッドプールを作成するときは、実際には次のようにします. corePoolSize:スレッドプールで永続的に保持されるコアスレッドの数は、setCorePoolSize関数で動的に変更できます. runnableTaskQueue:実行待ちのタスクを保存するためのブロックキュー.ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、PriorityBlockingQueue のブロックキューを選択できます. maximumPoolSize:スレッドプールで作成できる最大スレッド数.setMaximumPoolSize関数で を動的に変更できます. ThreadFactory:スレッドを作成するファクトリを設定します. RejectedExecutionHandler:キューとスレッドプールがいっぱいになると、新しいタスクに参加できなくなり、スレッドプールが飽和します.このパラメータは飽和ポリシーを表し、デフォルトではAbortPolicyであり、新しいタスクを処理できない場合に例外を放出することを示します.JDK 1.5が提供する4つのポリシー:AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy keepAliveTime:デフォルトでは、非コアスレッドがアイドル状態になった後の生存時間を指し、コアスレッドに影響を及ぼさず、setKeepAliveTime関数で動的に変更できます.++ただし、allowCoreThreadTimeOut関数を呼び出すことで、この変数がコアスレッドに作用するように設定できます.++ TimeUnit:keepAliveTimeの時間単位.
参考:方飛
0 x 02 ThreadPoolExecutorのワークフロースレッドプール内のスレッド数がcorePoolSizeより小さく、空きスレッドがない場合、新しいスレッド実行タスクが作成され、空きスレッドが存在し、aliveである場合、スレッドが多重化されます. スレッドプール内のスレッド数がcorePoolSizeに等しく、空きスレッドがなく、キューが満たされていない場合、タスクはブロックキューに追加され、 の実行を待つ.スレッドプール内のスレッド数がcorePoolSizeに等しく、空きスレッドがなく、キューがいっぱいである場合、スレッド数がmaximumPoolSizeより小さい場合、新しいスレッド実行タスクが作成されます. スレッドプール内のスレッド数がcorePoolSizeに等しく、空きスレッドがなく、キューがいっぱいである場合、スレッド数がmaximumPoolSizeより大きい場合、対応する拒否ポリシーに従ってタスクを処理する keepAliveTimeが0より大きい場合、デフォルトではコアスレッド以外で現在のタスクを実行すると、すぐに回収されるのではなく、指定された時間を待つことになります.この時間内にタスクが必要でない場合は回収されます.そうでない場合は、タスクを実行します.
まとめて、もっと簡単に率直に言います. corePoolSize範囲内のスレッド を優先的に使用は、次にブロックキューである.(実行スレッド数がcorePoolSizeより大きい場合はキューに追加) 最後はmaximumPoolSizeの範囲内のスレッドです.(キューもいっぱいで実行スレッド数がmaximumPoolSize未満の場合、新規スレッド) 0 x 03 Executorで提供されるスレッドプールの作成に関する5つの方法
1.newFixedThreadPool
nThreads=3の場合、コアスレッド数と最大スレッド数が3であり、この方法で作成されたスレッドプールブロックキューの長さはInteger.MAX_であることを示します.VALUE.この場合、追加されたタスクがいくらあっても、スレッドプールは3つのスレッドしか提供されず、これら3つは空きの有無にかかわらずオンライン・スレッドプールで破棄されません.FixedThreadPoolスレッドプールは、タスクが密集しており、タスクの量が少ない場合に適しています.ただし、nThreadsの数が小さすぎると、ブロックされたキューがいっぱいになり、タスクがRejectされる可能性があることに注意してください.
2.newSingleThreadExecutor
この方法で作成されたスレッドプールを使用して、newSingleThreadExecutor内部ではF i n a l i z a bleDelegatedExecutorServiceを使用してスレッドプールを作成しています.F i n a l i z a bleDelegatedExecutorServiceはDelegatedExecutorServiceから継承されています.このクラスはパッケージクラスで、ThreadPoolExecutorをパッケージ化した後、ExecutorServiceのインタフェースメソッドが露出します.したがって、newSingleThreadExecutorが返すExecutorServiceは、newFixedThreadPool(1)とは異なり、ExecutorServiceが再構成されないことを保証します.
3.newCachedThreadPool
この方法で作成したスレッドプールは、コアスレッド数が0、最大スレッド数がInteger.MAX_であることを示します.VALUEなので、このスレッドプールのスレッドが空き状態になると60秒しか保持されず、回収されます.SynchronousQueueは要素を格納できないブロックキューであり、各put操作はtake操作を待たなければならない.そうしないと要素の追加を続行できないため、このようなスレッドプールにタスクをコミットする際に、空きスレッドがkeepAliveにある場合は、そのスレッドを直接使用しなければならない.そうしないと、新しいスレッドが作成される.このスレッドプールは、タスクが簡単で時間がかかるシナリオに適しています.
4.newScheduledThreadPool
この方法は他の方法とは少し異なり,その内部ではScheduledThreadPoolExecutorの構造方法を直接呼び出し,ScheduledThreadPoolExecutorはThreadPoolExecutorのサブクラスであり,その内部ではタイミング実行の論理を実現している.newFixedThreadPoolと似ていますが、このスレッドプールではDelayedWorkQueueが使用されています.DelayedWorkQueueは優先キューで、スタック構造に基づいて、キューの先頭は常に実行時間が最も前です.
5.newWorkStealingPool
この方法はJDK 8に新しく追加された方法で、この方法の内部にはForkJoinPoolが作成されています(このスレッドプールはForkJoinTaskと組み合わせて使用されています).ForkJoinとは?実は分治、合併で、大きなタスクを複数の小さなサブタスクに分けて、それぞれ実行して最終的に結果をまとめて合併します.では、WorkStealingとは何ですか.これは、自分のタスクを完了したワークスレッドが、アイドルではなく他のスレッドから保留中のタスクを盗むスケジューリングポリシーです.このスレッドプールはパラレル実行であり、デフォルトのパラレル量はプロセッサコア数であり、タスクは複数のプロセッサ間で分割されます.紙幅の問題は,もう展開しないで,今度は単独でこれを書きます.
0 x 03カスタムスレッドプール
Part1
アリのJava開発マニュアルではExecutorsを使用してスレッドプールを作成することをお勧めしませんが、カスタム作成をお勧めします.
Part2
カスタム作成には、次のような問題があります.
このようにスレッドプールを作成するのは危険であり、キューがいっぱいになり、追加タスクが拒否される可能性があります.したがって,ブロックキューがいっぱいになった場合の処理対策を考慮して,RejectedExecutionHandlerを自分で設定して処理することができる.またcorePoolSize=maximumPoolSizeの場合、このときkeepAliveTimeパラメータを指定することは意味がありません.これらのスレッドは永続的に保持されます.上から次のように変更すると、ブロックキューがいっぱいになることを減らすことができます.
これにより、コアスレッドが動作し、ブロックキューもいっぱいになると、キュー内のタスクを実行するために新しいスレッドが作成されます.
Part3
以下のような構成にも問題がある.
この構造はmaximumPoolSizeをほぼ失効させ,スレッドプールスレッドの数はずっと5である.
Part4
以下のような構成にも問題がある.
この構成ではcorePoolSizeとmaximumPoolSizeがほぼ失効し、新しく追加されたタスクはキューがいっぱいになるまでブロックキューに直接追加され、新しいスレッドが作成されますが、キューがいっぱいになるとOutOfMemoryExceptionになっている可能性があります.つまり、新しく追加したタスクは実行されない可能性があります.
Part5
また、次のようなものがあります.
タスクの数について実行時間が明確に認識されていない場合、スレッドプールの作成もRejectにつながる可能性があります.たとえば、次のコードを実行します.
各タスクの実行時間が長すぎるため、スレッドプールの数はすぐに最大になり、101番目のタスクに追加すると例外が放出されます.
まとめ:スレッドプールをカスタマイズする場合は、ビジネスニーズに基づいてタスク数を推定し、実行速度を合理的に設定し、スレッドプールのパラメータを合理的に構成する必要があります.さらにRejectedExecutionHandlerをカスタマイズすることが望ましい
参照先:https://www.cnblogs.com/vhua/p/5297587.html https://juejin.im/entry/5afe36a46fb9a07aa213965b
前のブログで述べたように、Executorsはツールクラスです.スレッドプールを作成するときは、実際には次のようにします.
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds,runnableTaskQueue, handler);
参考:方飛
0 x 02 ThreadPoolExecutorのワークフロー
まとめて、もっと簡単に率直に言います.
1.newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
nThreads=3の場合、コアスレッド数と最大スレッド数が3であり、この方法で作成されたスレッドプールブロックキューの長さはInteger.MAX_であることを示します.VALUE.この場合、追加されたタスクがいくらあっても、スレッドプールは3つのスレッドしか提供されず、これら3つは空きの有無にかかわらずオンライン・スレッドプールで破棄されません.FixedThreadPoolスレッドプールは、タスクが密集しており、タスクの量が少ない場合に適しています.ただし、nThreadsの数が小さすぎると、ブロックされたキューがいっぱいになり、タスクがRejectされる可能性があることに注意してください.
2.newSingleThreadExecutor
この方法で作成されたスレッドプールを使用して、newSingleThreadExecutor内部ではF i n a l i z a bleDelegatedExecutorServiceを使用してスレッドプールを作成しています.F i n a l i z a bleDelegatedExecutorServiceはDelegatedExecutorServiceから継承されています.このクラスはパッケージクラスで、ThreadPoolExecutorをパッケージ化した後、ExecutorServiceのインタフェースメソッドが露出します.したがって、newSingleThreadExecutorが返すExecutorServiceは、newFixedThreadPool(1)とは異なり、ExecutorServiceが再構成されないことを保証します.
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
3.newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
この方法で作成したスレッドプールは、コアスレッド数が0、最大スレッド数がInteger.MAX_であることを示します.VALUEなので、このスレッドプールのスレッドが空き状態になると60秒しか保持されず、回収されます.SynchronousQueueは要素を格納できないブロックキューであり、各put操作はtake操作を待たなければならない.そうしないと要素の追加を続行できないため、このようなスレッドプールにタスクをコミットする際に、空きスレッドがkeepAliveにある場合は、そのスレッドを直接使用しなければならない.そうしないと、新しいスレッドが作成される.このスレッドプールは、タスクが簡単で時間がかかるシナリオに適しています.
4.newScheduledThreadPool
この方法は他の方法とは少し異なり,その内部ではScheduledThreadPoolExecutorの構造方法を直接呼び出し,ScheduledThreadPoolExecutorはThreadPoolExecutorのサブクラスであり,その内部ではタイミング実行の論理を実現している.newFixedThreadPoolと似ていますが、このスレッドプールではDelayedWorkQueueが使用されています.DelayedWorkQueueは優先キューで、スタック構造に基づいて、キューの先頭は常に実行時間が最も前です.
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
//ScheduledThreadPoolExecutor ThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
5.newWorkStealingPool
この方法はJDK 8に新しく追加された方法で、この方法の内部にはForkJoinPoolが作成されています(このスレッドプールはForkJoinTaskと組み合わせて使用されています).ForkJoinとは?実は分治、合併で、大きなタスクを複数の小さなサブタスクに分けて、それぞれ実行して最終的に結果をまとめて合併します.では、WorkStealingとは何ですか.これは、自分のタスクを完了したワークスレッドが、アイドルではなく他のスレッドから保留中のタスクを盗むスケジューリングポリシーです.このスレッドプールはパラレル実行であり、デフォルトのパラレル量はプロセッサコア数であり、タスクは複数のプロセッサ間で分割されます.紙幅の問題は,もう展開しないで,今度は単独でこれを書きます.
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
0 x 03カスタムスレッドプール
Part1
アリのJava開発マニュアルではExecutorsを使用してスレッドプールを作成することをお勧めしませんが、カスタム作成をお勧めします.
Part2
カスタム作成には、次のような問題があります.
// 。
new ThreadPoolExecutor(5,5,0,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(1000));
このようにスレッドプールを作成するのは危険であり、キューがいっぱいになり、追加タスクが拒否される可能性があります.したがって,ブロックキューがいっぱいになった場合の処理対策を考慮して,RejectedExecutionHandlerを自分で設定して処理することができる.またcorePoolSize=maximumPoolSizeの場合、このときkeepAliveTimeパラメータを指定することは意味がありません.これらのスレッドは永続的に保持されます.上から次のように変更すると、ブロックキューがいっぱいになることを減らすことができます.
new ThreadPoolExecutor(5,50,0,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(1000));
// RejectExceptionHandler
new ThreadPoolExecutor(5,50,0,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(1000),handler);
これにより、コアスレッドが動作し、ブロックキューもいっぱいになると、キュー内のタスクを実行するために新しいスレッドが作成されます.
Part3
以下のような構成にも問題がある.
new ThreadPoolExecutor(5,50,0,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>());
この構造はmaximumPoolSizeをほぼ失効させ,スレッドプールスレッドの数はずっと5である.
Part4
以下のような構成にも問題がある.
new ThreadPoolExecutor(0, 50, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
この構成ではcorePoolSizeとmaximumPoolSizeがほぼ失効し、新しく追加されたタスクはキューがいっぱいになるまでブロックキューに直接追加され、新しいスレッドが作成されますが、キューがいっぱいになるとOutOfMemoryExceptionになっている可能性があります.つまり、新しく追加したタスクは実行されない可能性があります.
Part5
また、次のようなものがあります.
new ThreadPoolExecutor(5, 100, 60, TimeUnit.SECONDS, new SynchronousQueue<>());
タスクの数について実行時間が明確に認識されていない場合、スレッドプールの作成もRejectにつながる可能性があります.たとえば、次のコードを実行します.
ExecutorService executorService =
new ThreadPoolExecutor(5, 100, 0, TimeUnit.MILLISECONDS, new SynchronousQueue<>());
for (long i = 0; i <30; i++) {
executorService.execute(() -> {
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(i);
}
各タスクの実行時間が長すぎるため、スレッドプールの数はすぐに最大になり、101番目のタスクに追加すると例外が放出されます.
rejected from java.util.concurrent.ThreadPoolExecutor@43556938[Running, pool size = 100, active threads = 100, queued tasks = 0, completed tasks = 0]
まとめ:スレッドプールをカスタマイズする場合は、ビジネスニーズに基づいてタスク数を推定し、実行速度を合理的に設定し、スレッドプールのパラメータを合理的に構成する必要があります.さらにRejectedExecutionHandlerをカスタマイズすることが望ましい
参照先:https://www.cnblogs.com/vhua/p/5297587.html https://juejin.im/entry/5afe36a46fb9a07aa213965b