今回は、JUCのスレッドプールについて説明します。ThreadPoolの場合、スレッドの束を作成したことを理解してから、これらのスレッドに指定したことを実行させます。
最初に継承図を見てください。
実行者はスレッドを作成するための3つの方法を実行します。
ExecutorService threadPool = Executors.newFixedThreadPool(5); //固定容量
ExecutorService threadPool = Executors.newSingleThreadExecutor(); //单例的、单个线程的线程池
ExecutorService threadPool = Executors.newCachedThreadPool(); //缓存的 即超出就自动创建线程的
次に、これら3つの違いを説明します。
まず、最初の固定容量スレッドプールを確認しますExecutors.newFixedThreadPool(5);
。
最初にコードを確認します。
/**
* 主要特点:线程复用;控制最大并发数;管理线程。
*
* @author Cocowwy
* @create 2020-05-05-20:20
* Executor/ExecutorServic(Interface)
* Executors 线程池的工具类
*/
public class MyThreadPoolDemo {
public static void main(String[] args) {
//一池五个受理线程
ExecutorService threadPool = Executors.newFixedThreadPool(5); //看源码是LinkedBlockingQueue<Runnable>()
try {
//模拟10个用户办理业务,但是只有5个受理窗口
for (int i = 0; i < 10; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "办理业务");
});
Thread.sleep(400);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown(); //关闭线程池
}
}
}
結果は次のとおりです。
次に、スレッドが短時間スリープするコードを追加します。
public class MyThreadPoolDemo {
public static void main(String[] args) {
//一池五个受理线程
ExecutorService threadPool = Executors.newFixedThreadPool(5); //看源码是LinkedBlockingQueue<Runnable>()
try {
//模拟10个用户办理业务,但是只有5个受理窗口
for (int i = 0; i < 10; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "办理业务");
});
Thread.sleep(400);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown(); //关闭线程池
}
}
}
ここでは、各ビジネスが整然と処理されていることがわかります。これは固定サイズのスレッドプールであり、スレッドは毎回このスレッドプールから取得されていることがわかります。
これは2番目のシングルトンスレッドプールです:ExecutorService threadPool = Executors.newSingleThreadExecutor(); //一池1个受理线程
public class MyThreadPoolDemo {
public static void main(String[] args) {
// ExecutorService threadPool = Executors.newFixedThreadPool(5); //一池五个受理线程,看源码是LinkedBlockingQueue<Runnable>()
ExecutorService threadPool = Executors.newSingleThreadExecutor(); //一池1个受理线程
try {
//模拟10个用户办理业务,但是只有5个受理窗口
for (int i = 0; i < 10; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "办理业务");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown(); //关闭线程池
}
}
}
スレッドは常にビジネスを受け入れてきたことがわかります。
次は3番目のスレッドプールです:ExecutorService threadPool = Executors.newCachedThreadPool(); //一池N个受理线程 可扩展的
次のコード:
public class MyThreadPoolDemo {
public static void main(String[] args) {
// ExecutorService threadPool = Executors.newFixedThreadPool(5); //一池五个受理线程,看源码是LinkedBlockingQueue<Runnable>()
// ExecutorService threadPool = Executors.newSingleThreadExecutor(); //一池1个受理线程
ExecutorService threadPool = Executors.newCachedThreadPool(); //一池N个受理线程
try {
//模拟10个用户办理业务,但是只有5个受理窗口
for (int i = 0; i < 10; i++) {
threadPool.execute(() -> {
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t" + "办理业务");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown(); //关闭线程池
}
}
}
最初に効果を確認します。
上記のコードでは、スレッドプールから取得したスレッドが0.4秒間スリープしていることがわかりますが、より多くのスレッドが作成されていることがわかります。したがって、リクエストが多すぎる場合にわかります。スレッドが作成されます。
上記はスレッドプールの単純なAPIです。
次に、スレッドプールのソースコードを
確認します。最初に newFixedThreadPool()
のソースコードをクリックして表示します。次にnewSingleThreadExecutor()
のソースコードをクリックして表示します。次にnewCachedThreadPool()のソースコードをクリックして表示します。
综上所述,返回的实际上只是一个ThreadPoolExecutor
(可以看看继承图),利用构造器传入的不同的参数而已,而且我们也能发现底层是阻塞队列。
同时说明我们也可以通过ThreadPoolExecutor`来创建线程池,Executors只是一个创建线程池的工具类,实际上返回的还是ThreadPoolExecutor。
接着我们继续点ThreadPoolExecutor
:
接着再点进这this,我们可以看到它有七个参数,:
下图是这七大参数的解释:
接下来结合下图理解理解上述的7大参数:
首先看看线程池的底层工作原理图:
看上图以及参数解析对照我们可以知道maximumPool包含corePool,maximumPool表示最多能放的线程数,而corePool表示的就是线程的常驻数,可以理解为银行的有最多有5个受理窗口,但是常用的却只有2个,而候客区就相当于我们的阻塞队列(BlockingQueue),那当我们的阻塞队列满了之后,handle拒绝策略出来了,相当于银行门口立了块牌子,上面写着不办理后面的业务了!然后当客户都办理的差不多了,此时多出来(在corePool的基础上扩容的窗口)的窗口在经过keepAliveTime的时间后就关闭了,重新恢复到corePool个受理窗口。
次に、
スレッドプールのワークフローを要約します。まず、スレッドプールがタスクを受け取り、コアスレッドの数がいっぱいかどうかを最初に判断し、いっぱいでない場合はブロックキューに入れます。ブロックキューがいっぱいでない場合、これらのタスクはブロックキューに入れられます。スレッド数がいっぱいの場合は、スレッド数を最大スレッド数まで拡張します。最大スレッド数もいっぱいの場合は、拒否ポリシーです。これは、スレッドプールの4つの主要なステップです。顧客をピックアップし、キューに入れ、スレッドを拡張し、戦略を拒否します。
あなたはまたこの絵を見ることができます:
実際の開発でどのように構成しますか?
どうして?
たとえば、話をnewSingleThreadExecutor(); ;
してExecutors.newCachedThreadPool( );
スレッドプールを作成する前に、ソースを確認します。
//这是Single的
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
}
//点进去LinkedBlockQueue
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
//这是Cahed的
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
以下のために)(newSingleThreadExecutor ; LinkedBlockQueueの長さがありInteger.MAX_VALUEで、かつ
用newCachedThreadPool() 、の値maximumPoolがある Integer.MAX_VALUEで!!
どちらもOOM例外を引き起こします!
上記のパラメータについて説明した後、カスタムスレッドプールを見てみましょう。
public class MyThreadPoolDemo {
public static void main(String[] args) {
//自定义线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2,
5,
2L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3), //不写的话默认也是Integer.MAX_VALUE
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());//默认的拒绝策略
try {
//模拟10个用户办理业务,但是只有5个受理窗口
for (int i = 0; i < 9; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "办理业务");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown(); //关闭线程池
}
}
threadPoolはカスタムスレッドプールです。上記のパラメータに接続している人は、スレッドプールでサポートされる同時実行の最大量がmaximumPool + Queueのサイズ、つまり5 + 3 = 8であり、サイズを超えた後になることを知っておく必要があります。エラー:java.util.concurrent.RejectedExecutionException
例外の実行を拒否します
次に、スレッドプールの4つの主要な拒否戦略を見てみましょう。上記はJDKのデフォルトの拒否戦略です。
次に、他の3つの戦略の結果を見てみましょう。上記のコードの拒否戦略を2番目のものに変更しnew ThreadPoolExecutor.CallerRunsPolicy()
ます。 3 new ThreadPoolExecutor.DiscardOldestPolicy()
:
エラーは報告されません。
4番目new ThreadPoolExecutor.DiscardPolicy()
:
エラーは報告されません。
上記の戦略はRejectedExecutionHandler
インターフェースから継承されます。
最後に、maximumPoolSizeを適切に設定する方法
System.out.println(Runtime.getRuntime().availableProcessors()); //8核
通常、CPUコアの数に1を加えた数に設定します。
上記はこのスレッドプールの説明です。不合理なことがあれば、下にメッセージを残してください!