ThreadPoolExecutorの使用
ThreadPoolExecutorは、次の4つの構築方法を提供します。
最後の構築方法(パラメーターが最も多い方法)を使用して、そのパラメーターを説明します。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler ) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
シリアルナンバー | 名前 | の種類 | 意味 |
---|---|---|---|
1 | corePoolSize | int | コアスレッドプールサイズ |
2 | maximumPoolSize | int | 最大スレッドプールサイズ |
3 | keepAliveTime | 長いです | スレッドの最大アイドル時間 |
4 | 単位 | TimeUnit | 時間単位 |
5 | workQueue | BlockingQueue | スレッド待機キュー |
6 | threadFactory | ThreadFactory | スレッド作成ファクトリ |
7 | ハンドラ | RejectedExecutionHandler | 拒否戦略 |
各パラメータの役割を知った後、私たちは期待に応えるスレッドプールの構築を開始しました。まず、JDKによって事前定義されたいくつかのスレッドプールを確認します。
1つの事前定義されたスレッドプール
1. FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
- corePoolSizeはmaximumPoolSizeと同じです。つまり、そのスレッドはすべてコアスレッドであり、固定サイズのスレッドプールであるという利点があります。
- keepAliveTime = 0このパラメーターはデフォルトではコアスレッドには無効であり、FixedThreadPoolはすべてコアスレッドです。
- workQueueはLinkedBlockingQueue(無制限のブロッキングキュー)であり、キューの最大値はInteger.MAX_VALUEです。タスクの送信速度が引き続きタスクの処理速度を超えると、キューに多くの輻輳が発生します。キューが非常に大きいため、ポリシーが拒否される前にメモリがオーバーフローする可能性が非常に高くなります。その欠点は;
- FixedThreadPoolのタスク実行は無秩序です。
該当するシナリオ:Webサービスの瞬間的なピーククリッピングに使用できますが、長期間の継続的なピーク状態によって引き起こされるキューの輻輳に注意する必要があります。
2. CachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
- corePoolSize = 0、maximumPoolSize = Integer.MAX_VALUE、つまり、スレッドの数はほぼ無制限です。
- keepAliveTime = 60sの場合、スレッドは60秒間アイドル状態になると自動的に終了します。
- workQueueはSynchronousQueue同期キューです。このキューはバトンに似ています。CachedThreadPoolスレッドは無制限に作成されるため、キューは同時に配信される必要があります。待機中のキューはありません。SynchronousQueueを使用してください。
- 該当するシナリオ:要求を受け入れるときのNetty NIOなど、短時間で多数のタスクを高速処理するには、CachedThreadPoolを使用できます。
2. SingleThreadPool
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}
この外観はnewFixedThreadPool(1)です。FinalizableDelegatedExecutorServiceパッケージの追加レイヤーがあります。このレイヤーの用途は何ですか?説明するドームを作成します。
public static void main(String[] args) {
ExecutorService fixedExecutorService = Executors.newFixedThreadPool(1);
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) fixedExecutorService;
System.out.println(threadPoolExecutor.getMaximumPoolSize());
threadPoolExecutor.setCorePoolSize(8);
ExecutorService singleExecutorService = Executors.newSingleThreadExecutor();
// 运行时异常 java.lang.ClassCastException
// ThreadPoolExecutor threadPoolExecutor2 = (ThreadPoolExecutor) singleExecutorService;
}
比較から、FixedThreadPoolはThreadPoolExecutorに変換でき、そのスレッドプールを構成できますが、SingleThreadExecutorはパッケージ化後に正常に変換できないことがわかります。したがって、SingleThreadExecutorを設定した後は変更できず、実際のSingleが実現されます。
2つのカスタムスレッドプール
Ali標準では、スレッドプールを作成する2番目の方法も推奨しています。
以下は、制限付きキュー、カスタムThreadFactory、および拒否戦略を使用したカスタムスレッドプールのデモです。
public class ThreadTest {
public static void main(String[] args) throws InterruptedException, IOException {
int corePoolSize = 2;
int maximumPoolSize = 4;
long keepAliveTime = 10;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
ThreadFactory threadFactory = new NameTreadFactory();
RejectedExecutionHandler handler = new MyIgnorePolicy();
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit,
workQueue, threadFactory, handler);
executor.prestartAllCoreThreads(); // 预启动所有核心线程
for (int i = 1; i <= 10; i++) {
MyTask task = new MyTask(String.valueOf(i));
executor.execute(task);
}
System.in.read(); //阻塞主线程
}
static class NameTreadFactory implements ThreadFactory {
private final AtomicInteger mThreadNum = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "my-thread-" + mThreadNum.getAndIncrement());
System.out.println(t.getName() + " has been created");
return t;
}
}
public static class MyIgnorePolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
doLog(r, e);
}
private void doLog(Runnable r, ThreadPoolExecutor e) {
// 可做日志记录等
System.err.println( r.toString() + " rejected");
// System.out.println("completedTaskCount: " + e.getCompletedTaskCount());
}
}
static class MyTask implements Runnable {
private String name;
public MyTask(String name) {
this.name = name;
}
@Override
public void run() {
try {
System.out.println(this.toString() + " is running!");
Thread.sleep(3000); //让任务执行慢点
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public String getName() {
return name;
}
@Override
public String toString() {
return "MyTask [name=" + name + "]";
}
}
}
出力は次のとおりです。