個人プロフィール
著者は河源の3年生です。以下のメモは、独学の道での著者の表面的な経験です。間違いがあれば訂正してください。今後もメモを改善していきます。より多くのJava愛好家が始めるのを助けるために。
記事ディレクトリ
Javaスレッドプールと原則
スレッドプールの利点
- スレッドの再利用は、スレッドの再作成と破壊を回避するために実現できます。スレッドの作成と破棄は、CPUで非常にコストがかかります。
- 作成できるスレッドの最大数を制限し、自分のマシンのパフォーマンスに応じてスレッドプールのパラメーターを動的に調整して、アプリケーションのパフォーマンスを向上させることができます。
- 時限実行や同時番号制御などの機能を提供します。
- スレッドの統合管理。
スレッドプールを作成する5つの方法
- 1:キャッシュスレッドプール(非推奨)
- 2:固定容量のスレッドプール(非推奨)
- 3:シングルスレッドプール(非推奨)
- 4:スケジュールされたタスクスレッドプール(非推奨)
- 5:ThreadPoolExecutor構築メソッドを使用してスレッドプールを作成します(Alibaba開発マニュアルを強くお勧めします)
スレッドプールを作成する最初の4つの方法はすべて、 Executorsの静的メソッドによって作成されます。
キャッシュされたスレッドプールCachedThreadPool
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int finalI = i;
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
}
});
}
キャッシュされたスレッドプールの使用が非推奨になるのはなぜですか?ソースコード分析
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue());
}
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
}
- 上記の2つのコードスニペットから、CachedThreadPoolのmaximumPoolSizeは整数2147483647の最大値であることがわかります。これは、スレッドを無期限に作成することと同じであり、スレッドを作成するにはメモリが必要であり、メモリオーバーフローが発生します。非常に大量のメモリを備えた多数のスレッド。
固定スレッドプールFixedThreadPool
- newFixedThreadPool(int num)、numは、指定する固定スレッドの数です。
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
final int finalI = i;
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
}
});
}
出力:
pool-1-thread-5<thread->run>4
pool-1-thread-4<thread->run>3
pool-1-thread-5<thread->run>5
pool-1-thread-3<thread->run>2
pool-1-thread-3<thread->run>8
pool-1-thread-3<thread->run>9
pool-1-thread-2<thread->run>1
pool-1-thread-1<thread->run>0
pool-1-thread-5<thread->run>7
pool-1-thread-4<thread->run>6
- スレッドの再利用に一役買っていることがわかります。
FixedThreadPoolが固定スレッドプールである理由 ソースコード分析
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
}
- このソースコードから、コアスレッドの数(corePoolSize)とスレッドの最大数(maximumPoolSize)は両方ともnThreadsであることがわかります。これは、この方法でのみ、スレッドプールが拡張されず、スレッドの数が拡張されるためです。修正されます。
シングルスレッドプールSingleThreadExecutor
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int finalI = i;
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
}
});
}
SingleThreadExecutorにスレッドが1つしかないのはなぜですか?ソースコード分析
public static ExecutorService newSingleThreadExecutor() {
return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()));
}
- このソースコードから、コアスレッドの数(corePoolSize)とスレッドの最大数(maximumPoolSize)は両方とも1であるため、1つのスレッドしか含まれていないことがわかります。
スケジュールされたタスクスレッドプールScheduledThreadPool
int initDelay=10; //初始化延时
int period=1;//初始化延迟过了之后,每秒的延时
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"<thread->run>");
}
},initDelay,period, TimeUnit.SECONDS);
- このコードの効果は次のとおりです。プログラムの実行後、10秒待ってから最初の結果を出力し、その後1秒ごとに結果を出力します。
ScheduledThreadPoolが非推奨になるのはなぜですか?ソースコード分析
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, 2147483647, 10L, TimeUnit.MILLISECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue());
}
- ScheduledThreadPoolのスレッドの最大数(maximumPoolSize )は整数2147483647の最大値であり、スレッドを無期限に作成するのと同じであり、スレッドを作成するにはメモリが必要であるため、メモリオーバーフローが発生し、一般的なマシンはそれほど大きくないことがわかります。 。そのような多数のスレッドを作成するためのメモリ。
ThreadPoolExecutorはスレッドプールを作成します(非常に推奨)
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20,
2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 12; i++) {
final int finalI = i;
threadPoolExecutor.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
}
});
}
ThreadPoolExecutorの7つのパラメーターの詳細な説明
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
}
- corePoolSize:コアスレッドの数。これらのスレッドが作成されると、それらは破棄されず、常に存在します。スレッドプールにはデフォルトでスレッドがありません。タスクが到着すると、スレッドはThreadFactoryを介して作成され、常に存在します。
- maximumPoolSize:スレッドの最大数。非コアスレッドの数=maximumPoolSize-corePoolSize、非コアスレッドの数は、実際には破壊される可能性のあるスケーラブルスレッドの数です。
- keepAliveTime:非コアスレッドのアイドルサバイバル時間。拡張によって生成された非コアスレッドの数がkeepAliveTimeの後もアイドル状態のままである場合、これらの非コアスレッドは破棄されます。
- 単位:keepAliveTimeの時間単位。例:秒
- workQueue:待合室。タスク>corePoolSizeが来ると、タスクはworkQueueのブロッキングキューに格納され、他のスレッドが処理するのを待ちます。
- threadFactory:スレッドファクトリ。スレッドを作成する方法。
- ハンドラー:ポリシーを拒否します。>スレッドの最大数+workQueueの容量になると、拒否ポリシーが実行されます
workQueue
- ArrayBlockingQueue:制限付きブロッキングキュー。キューにはサイズ制限があり、容量を超えると、拡張または拒否ポリシーがトリガーされます。
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
- LinkedBlockingQueue:無制限のブロッキングキュー。キューにはサイズ制限がないため、メモリオーバーフローが発生する可能性があります。
public LinkedBlockingQueue() {
this(2147483647);
}
ハンドラ
- AbortPolicy:例外を直接スローします
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() {
}
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
}
}
- DiscardPolicy:何もしません。タスクを黙って破棄する
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() {
}
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
- DiscardOldestPolicy:最も古いタスクを破棄します
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() {
}
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
- CallerRunsPolicy:タスクを送信するスレッドにタスクを処理させます
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() {
}
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
threadFactory
ThreadFactory threadFactory = Executors.defaultThreadFactory();
threadFactory.newThread(new Runnable() {
@Override
public void run() {
System.out.println("threadFactory");
}
}).start();
拒否ポリシーとスレッドプールの拡張をトリガーする方法は?
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20,
2L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 26; i++) {
//并发数26
final int finalI = i;
threadPoolExecutor.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+"<thread->run>"+ finalI);
}
});
}
/**
* 核心线程数=10,最大线程数=20,故可扩容线程数=20-10
* BlockingQueue的大小为5,故等待区的大小为5,也就是当并发数<=核心线程数+5不会扩容,并发数大于16才会扩容
*
* 触发扩容:并发数>核心线程数+阻塞队列的大小
* 对于这段代码,如果来了26个并发,10个并发会被核心线程处理,5个会在等待区,剩下11个会因为等待区满了而触发扩容
* 因为这里最多能够扩容10个,这里却是11个,所以会触发拒绝策略
*/
このコードが拒否ポリシーをトリガーするのはなぜですか
- このコードでは、26の同時実行性がある場合、10の同時実行性がコアスレッドによって処理され、5つが待機領域にあり、残りの11は待機領域がいっぱいであるため拡張をトリガーしますが、最大値はここで拡張できます。 10ですが、ここでは11なので、拒否ポリシーがトリガーされます。
拡張をトリガーする方法
- トリガーの拡張:同時実行性>コアスレッド(corePoolSize)+ブロッキングキュー(workQueue)サイズ
Javaを使用して、純粋に手動でスレッドプールを作成します
- これは次の記事にお任せしますので、お楽しみに