一.爲什麼要用線程池
線程池是一種多線程處理形式。
線程池做的工作只要是控制運行的線程數量,處理過程中將任務放入隊列,然後在線程創建後啓動這些任務,如果線程數量超過了最大數量,超出數量的線程排隊等候,等其他線程執行完畢,再從隊列中取出任務來執行
線程池的主要特點爲: 線程複用;控制最大併發數;管理線程
- 降低資源消耗。通過重複利用已創建的線程降低線程創建和銷燬造成的銷耗
- 提高響應速度。當任務到達時,任務可以不需要等待線程創建就能立即執行
- 提高線程的可管理性。線程是稀缺資源,如果無限制的創建,不僅會銷耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控
二.JDK1.8線程池架構
Java中的線程池是通過Executor框架實現的,該框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor這幾個類
Executor:
public interface Executor {
void execute(Runnable command);
}
Executor的官方定義是An object that executes submitted {@link Runnable} tasks 翻譯過來就是:執行提交的任務(Runnable形式)
ExecutorService:
ExecutorService是Executor的子接口,它是Executor的擴展,能提供管理終止的方法(shutdown),以及可以生成Future對象以跟蹤一個或多個異步任務的進度的方法
ThreadPoolExecutor:
ThreadPoolExecutor是線程池類,它提供幾種構造函數讓我們來創建線程池。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
線程池的參數非常重要,之後會有文章講。
Executors:
Executors 工具類提供了四種不同的線程池來幫助我們創建不同需求的線程池,其實質也是調用ThreadPoolExecutor的構造方法。
三.代碼示例:三種線程池
應用場景:銀行有n個櫃檯,有10個顧客辦理業務
public static void main(String[] args) {
//1.newFixedThreadPool 固定個數線程池
//ExecutorService threadPool = Executors.newFixedThreadPool(5);
//2.newSingleThreadExecutor 一池一線程
//ExecutorService threadPool = Executors.newSingleThreadExecutor();
//3.newCachedThreadPool 可擴容 有伸縮性 遇強則強
//ExecutorService threadPool = Executors.newCachedThreadPool();
try {
for (int i = 1; i <= 10 ; i++) { //模擬10個顧客,execute傳入參數是Runnable函數式接口
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"\t辦理業務");
});
//暫停400ms
try { TimeUnit.MILLISECONDS.sleep(400); }catch (Exception e){ e.printStackTrace(); }
}
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
首先我們創建線程池,這裏用的是Executors提供的方法,其次我們用循環模擬10個任務,讓線程池的線程來執行,最後在finally語句塊調用shutdown關閉
1.Executors.newFixedThreadPool(int n)
執行長期任務性能好,創建一個線程池,一池有N個固定的線程,有固定線程數的線程。可以通過將第一個語句註釋去掉運行看看結果:
pool-1-thread-1 辦理業務
pool-1-thread-2 辦理業務
pool-1-thread-3 辦理業務
pool-1-thread-4 辦理業務
pool-1-thread-5 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-2 辦理業務
pool-1-thread-3 辦理業務
pool-1-thread-4 辦理業務
pool-1-thread-5 辦理業務
Process finished with exit code 0
發現首先滿足了我們的需求,其次分佈很均勻,這是因爲我們用TimeUnit睡了一段時間,否則就會比較混亂。
2.newSingleThreadExecutor
一個任務一個任務的執行,一池一線程 這相當於只有一個櫃檯能辦理業務
"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" "-javaagent:D:\idea\IntelliJ IDEA 2019.2.3\lib\idea_rt.jar=55234:D:\idea\IntelliJ IDEA 2019.2.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;C:\Users\李肇京\IdeaProjects\JUC\out\production\JUC" JUC_01_SellTicket.ExecutorDemo.MyThreadPoolDemo
pool-1-thread-1 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-1 辦理業務
3.newCachedThreadPool
執行很多短期異步任務,線程池根據需要創建新線程,但在先前構建的線程可用時將重用它們。可擴容,遇強則強
- 此時,如果我們把暫停線程的語句加上,你會發現情況跟2一樣了,一直是一個線程來處理我們的任務,這是因爲線程池認爲當前情況下不需要多個線程處理
- 如果把暫停語句去掉,或者把任務數增大,線程池會自動擴容,這時就會有多個線程處理我們的任務。