執行緒池
執行緒池概述
執行緒池見名知意,就是指一個裝多個執行緒的池子,
為什么需要執行緒池
在沒有執行緒池的情況下,我們執行一個任務會創建一個執行緒,執行完畢后執行緒就會銷毀,如果有新的任務就需要重復這些步驟,所以執行緒池存在的意義就是在執行完一個任務之后,執行緒不會銷毀,并保存在執行緒池里面,如果有新的任務直接呼叫執行緒池里面的執行緒即可,
執行緒池的優點:
- 降低資源的消耗:通過重復利用現有的執行緒物件執行任務,避免了多次創建和銷毀的步驟
- 提高了相應速度:因為省去了創建執行緒的步驟
- 提供附加功能:執行緒池的擴展性可以使得我們以后加入一些自己的功能,比如說定時,延時
創建步驟
- 創建執行緒池物件
- 有任務需要執行緒物件執行的時候,創建執行緒物件去執行,執行完畢后不銷毀,存到執行緒池中
- 當所有任務都執行完畢,執行緒池就沒有存在的必要了,所以需要關閉執行緒池
執行緒池實作代碼
方法一:static Executors.newCachedThreadPool() 創建一個默認的執行緒池
public class ExecutorDemo {
public static void main(String[] args) throws InterruptedException {
/**
* Executors用于創建執行緒池
* ExecutorService用于操作執行緒池
*/
ExecutorService es=Executors.newCachedThreadPool();
es.submit(new Runnable() {
@Override
public void run() {
//任務1
System.out.println(Thread.currentThread().getName()+"執行緒1");
}
});
//任務2
es.submit(()->{System.out.println(Thread.currentThread().getName()+"執行緒2");});
//關閉執行緒池
es.shutdown();
}
}
結果是

但是這造成一個問題,以上程式并沒有體現出一個執行緒重復利用的思想,而是創建了兩個執行緒執行兩個任務,原因是:執行緒池遇到任務1時,創建了執行緒1執行,但是,任務1還沒執行完,執行緒沒有歸還的時候,任務2搶占到了執行權,這個時候執行緒池沒有多余的執行緒可以用,因此創建了執行緒2來執行任務2,
為了避免這個問題,需要讓程式沉睡一下,讓執行緒1有充足的時間執行完任務并歸還
代碼如下:
public class ExecutorDemo {
public static void main(String[] args) throws InterruptedException {
/**
* Executors用于創建執行緒池
* ExecutorService用于操作執行緒池
*/
ExecutorService es=Executors.newCachedThreadPool();
es.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"執行緒1");
}
});
//增加睡眠
Thread.sleep(100);
es.submit(()->{System.out.println(Thread.currentThread().getName()+"執行緒2");});
//關閉執行緒池
es.shutdown();
}
}
結果:

以上就實作了一個執行緒執行多個任務的案例,
方法二:static Executors.newFixedThreadPool(int nThreads) 創建一個指定最多執行緒數量的執行緒池
代碼如下:
public class ExecutorDemo2 {
public static void main(String[] args) throws InterruptedException {
/**
* newFixedThreadPool中的引數表示執行緒池中的最大執行緒數
*/
ExecutorService es = Executors.newFixedThreadPool(10);
es.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"執行緒1");
}
});
Thread.sleep(100);
es.submit(()->{System.out.println(Thread.currentThread().getName()+"執行緒2");});
//關閉執行緒池
es.shutdown();
}
}

可以發現即使讓程式進入睡眠仍然創建了兩個執行緒,這是因為newFixedThreadPool()方法要求,在創建完最大數量的執行緒數之前,只會新建執行緒,而不會重復利用舊執行緒,
方式三:自定義執行緒池
進入newCachedThreadPool() 可以發現方法內部也是通過呼叫ThreadPoolExecutor類來創建執行緒池的

因此可以直接根據該類自定義執行緒池

以上形參一共有7個,可以舉例來說明這些引數的作用
案例:假如現在有一家高級餐廳(執行緒池),實行員工顧客一對一服務,那么代表每有一名顧客,餐廳就需要招聘一名員工為其服務,但是老板基于成本考慮,餐廳不能無限制的招人,因為當顧客離去后,招聘的人太多了就浪費了成本,因此老板把員工分成了正式員工和臨時員工,正式員工會一直呆在餐廳作業,而臨時員工只會在正式員工繁忙的時候被招進來作業,當顧客較少時,臨時員工會被辭退,另外,即便有臨時員工的存在,一家餐廳的同時服務人數也是有上限的,等待被服務的顧客,只能在餐廳外排隊等候,同時餐廳還對排隊人數做了限制,如果排隊人數過多的話,超出范圍的人數會被拒絕服務,
現在根據本案例中提及的內容,和ThreadPoolExecutor構造方法的形參對號入座
| 形參名 | 事件解釋 | 術語解釋 |
|---|---|---|
| int corePoolSize | 正式員工的數量 | 核心執行緒數 |
| int maximumPoolSize | 餐廳最大的員工數量 | 最多執行緒數 |
| long keepAliveTime | 臨時員工空閑多長時間會被辭退(值) | 臨時執行緒數空閑時長 |
| TimeUnit unit | 臨時員工空閑多長時間會被辭退(單位) | 臨時執行緒數空閑時長(單位) |
| BlockingQueue |
排隊的客戶 | 阻塞佇列 |
| ThreadFactory threadFactory | 員工從哪里招 | 執行緒創建處 |
| RejectedExecutionHandler handler | 排隊人數過多時拒絕服務 | 定義拒絕策略 |
代碼實作:
public class ExecutorDemo3 {
public static void main(String[] args) throws InterruptedException {
//創建自定義執行緒池
ThreadPoolExecutor te = new ThreadPoolExecutor(
2,
4,
2,
TimeUnit.DAYS,
new ArrayBlockingQueue<>(6),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
for (int i = 0; i < 10; i++) {
te.submit(() -> {
System.out.println(Thread.currentThread().getName() + "正在執行");
});
}
//關閉執行緒池
te.shutdown();
}
}
結果:

可以看到正式員工和臨時員工都被呼叫了,但是服務人數最多為10人,因為服務人數上限=最大員工數+排隊人數,如果超過這個數量會報錯,這是因為超出的人員會被拒絕服務,

拒絕策略
RejectedExecutionHandler是jdk提供的一個任務拒絕策略結構
| 子類 | 說明 |
|---|---|
| ThreadPoolExecutor.AbortPolicy() | 丟棄任務并拋出RejectedExecutionException例外,是默認的策略, |
| ThreadPoolExecutor.DiscardPolicy() | 丟棄任務,但是不拋出例外 這是不推薦的做法, |
| ThreadPoolExecutor.DiscardOldestPolicy() | 拋棄佇列中等待最久的任務 然后把當前任務加入佇列中, |
| ThreadPoolExecutor.CallerRunsPolicy() | 呼叫任務的run()方法繞過執行緒池直接執行, |
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/547995.html
標籤:Java
