Java JUC併發之執行緒池詳解(重點)
十一、執行緒池(重點)
執行緒池 : 三大方法、7大引數、4種拒絕策略
池化技術
程式執行 => 本質:佔用系統的資源!
如何優化資源的使用? => 池化技術
常用的池:
- 執行緒池
- 連線池
- 記憶體池
- 物件池
- ...
注意: 經常建立、銷燬 會造成資源浪費
池化技術: 事先準備好一些資源,如果有人要用,就從這裡拿,用完之後還回來
執行緒池的好處:
即 : 執行緒複用、可以控制最大併發數、管理執行緒
執行緒池不允許使用executors去建立,而是通過ThreadPoolExecutor的方式
(讓寫的同學更加明確執行緒池的執行規則,規避資源耗盡的風險)詳細說明見
Executors 返回的執行緒池物件的弊端:
-
FixedThreadPool和SingleThreadPool:
允許的請求佇列長度為Integer.MAX_VALUE => 21億左右,會堆積大量的請求,導致OOM 【OOM : OutOfMemory,記憶體溢位】
-
CachedThreadPool 和 ScheduledThreadPool :
允許建立的執行緒數量為Integer.MAX_VALUE,可能會建立大量的執行緒,從而導致OOM.
三大方法:
- Executors.newSingleThreadExecutor() => 單一執行緒
- Executors.newFixedThreadPool(5) => 固定大小的執行緒池
- Executors.newCachedThreadPool() => 執行緒池大小由CPU處理能力決定 (彈性變化)
- Executors.newScheduledThreadPool(10) => 建立定長的執行緒池,而且支援定時以及週期性的任務執行
package com.liu.threadpool; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; // Executors 工具類 四大方法 public class TestThreadPool { public static void main(String[] args) { //ExecutorService threadPool = Executors.newSingleThreadExecutor(); // 單個執行緒 //ExecutorService threadPool = Executors.newFixedThreadPool(5); // 建立固定數目的執行緒 //ExecutorService threadPool = Executors.newCachedThreadPool(); // 根據CPU的能力來建立執行緒 遇強則強 ExecutorService threadPool = Executors.newScheduledThreadPool(10);// 建立定長的執行緒池,而且支援定時以及週期性的任務執行 try { for (int i = 1; i <= 10000; i++) { threadPool.execute(()->{ System.out.println(Thread.currentThread().getName() + " ok"); }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); } } }
執行緒池面試題!
四種實現方式 七大引數 四大拒絕策略
原始碼分析:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, // 21億
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
本質 : ThreadPoolExecutor() !!!
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue ) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(),
defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize, // 核心執行緒池大小
int maximumPoolSize, // 最大核心執行緒池大小
long keepAliveTime, // 空閒執行緒的存活時間
TimeUnit unit, // 超時等待的時間單位,時間到了會將執行緒釋放,只留下核心執行緒
BlockingQueue<Runnable> workQueue, // 阻塞佇列
ThreadFactory threadFactory, // 執行緒工廠
RejectedExecutionHandler handler // 拒絕策略) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
理解執行緒池的原理: 以銀行辦理業務為例:
手動建立執行緒池
package com.liu.threadpool;
import java.util.concurrent.*;
/**
* 四種拒絕策略
* 1. new ThreadPoolExecutor.AbortPolicy() 拒絕策略 銀行滿了,還有客人進來,不處理該業務並丟擲異常
* 2. new ThreadPoolExecutor.CallerRunsPolicy() // 拒絕策略 哪來的去哪裡! 由main執行緒處理
* 3. new ThreadPoolExecutor.DiscardPolicy() // 拒絕策略 佇列滿了 不會丟擲異常! 直接丟掉任務
* 4. new ThreadPoolExecutor.DiscardOldestPolicy() // 拒絕策略 佇列滿了 嘗試和最早的(執行了最長時間的執行緒)競爭,最後失敗了也不會丟擲異常
*/
public class TestThreadPool02 {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3), // 阻塞佇列
Executors.defaultThreadFactory(), // 執行緒工廠
new ThreadPoolExecutor.DiscardOldestPolicy() // 拒絕策略 佇列滿了 嘗試和最早的(執行了最長時間的執行緒)競爭,最後失敗了也不會丟擲異常
);
try {
// 最大承載 :Queue + Max
for (int i = 1; i <= 9; i++) {
// 使用了執行緒池之後,使用執行緒池來建立執行緒
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName() + " ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}
四種拒絕策略
- AbortPolicy() 銀行滿了,還有客人進來,不處理該業務並丟擲異常
- CallerRunsPolicy() 哪來的去哪裡! 由main執行緒 或其他執行緒 處理
- DiscardPolicy() 佇列滿了 不會丟擲異常! 直接丟掉任務
- DiscardOldestPolicy() 佇列滿了 嘗試和當前最老的執行緒執行的任務競爭,最後失敗了也不會丟擲異常
小結和拓展
-
最大執行緒到底該如何設定? 依據什麼來定義?(調優!)
-
CPI 密集型 CPU為多少核,就將最大執行緒池大小定義為多少
可以保持CPU的效率最高!
// 獲取CPU的核數 // CPU密集型、IO密集型 System.out.println(Runtime.getRuntime().availableProcessors());
-
IO 密集型 **判斷程式中十分佔用IO資源的的執行緒數 **一般設定為 2 倍
程式=> 15個大型任務 IO 十分佔用資源 => 設定為 30
-
package com.liu.threadpool;
import java.util.concurrent.*;
public class TestThreadPool02 {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,
Runtime.getRuntime().availableProcessors(), // cpu核數 即最大執行緒池大小
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3), // 阻塞佇列
Executors.defaultThreadFactory(), // 執行緒工廠
new ThreadPoolExecutor.DiscardOldestPolicy() // 拒絕策略 佇列滿了 嘗試和最早的(執行了最長時間的執行緒)競爭,最後失敗了也不會丟擲異常
);
try {
// 最大承載 :Queue + Max
for (int i = 1; i <= 9; i++) {
// 使用了執行緒池之後,使用執行緒池來建立執行緒
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName() + " ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}
本文來自部落格園,作者:{夕立君},轉載請註明原文連結:https://www.cnblogs.com/liuzhhhao/p/15016591.html