網站首頁 編程語言 正文
線程池3大方法
-
3種類型的線程池
-
單一線程線程池
newSingleThreadExecutor
public class ThreadPoolTest { public static void main(String[] args) { //創建一個只有一個線程的線程池 ExecutorService executorService = Executors.newSingleThreadExecutor(); try{ for(int i = 1;i <= 20;i++){ executorService.execute(()->{ System.out.println(Thread.currentThread().getName()+" ok"); }); } } finally { //線程池在不使用后 必須shutdowm executorService.shutdown(); } } }
打印
單一線程的線程池,都是一個線程在執行。
-
固定個數線程池
newFixedThreadPool
public class ThreadPoolTest { public static void main(String[] args) { //創建一個固定個數的線程池 ExecutorService executorService = Executors.newFixedThreadPool(5); try{ for(int i = 1;i <= 20;i++){ executorService.execute(()->{ System.out.println(Thread.currentThread().getName()+" ok"); }); } } finally { //線程池在不使用后 必須shutdowm executorService.shutdown(); } } }
打印
-
固定個數的線程池,永遠只有這幾個線程在執行
-
可伸縮的線程池
newCachedThreadPool
public class ThreadPoolTest { public static void main(String[] args) { //創建一個可伸縮的線程池 //根據當前任務數調整線程數量 ExecutorService executorService = Executors.newCachedThreadPool(); try{ for(int i = 1;i <= 20;i++){ executorService.execute(()->{ System.out.println(Thread.currentThread().getName()+" ok"); }); } } finally { //線程池在不使用后 必須shutdowm executorService.shutdown(); } } }
打印
可伸縮的線程池,線程數根據任務數變化。
線程池7大參數
-
先來看上面3種線程池的構造方法
//newSingleThreadExecutor public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } //newFixedThreadPool public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } //newCachedThreadPool public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } //上面的線程池創建會調用這個方法 //使用的默認線程工廠和默認的拒絕策略 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 //當線程池和阻塞隊列都滿了時的拒絕策略 ) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
從上面源碼可以看出,
newSingleThreadExecutor和newFixedThreadPool的線程數量雖然是固定的,但是阻塞隊列用的是new LinkedBlockingQueue()沒有設置大小,如果一直往里面添加任務的話,可能會出現OOM異常。
newCachedThreadPool使用的阻塞隊列雖然是new SynchronousQueue()同步隊列,但是設置的最大線程數是Integer.MAX_VALUE,如果使用不當會把CPU資源耗盡。
所以上面3種創建線程池的方法都不安全,可以自定義線程池。
-
自定義線程池
public class ThreadPoolTest { public static void main(String[] args) { /** * 自定義線程池 * 2個核心線程 * 最大線程數5個 * 最大空閑時間3 * 時間單位秒 * 阻塞隊列為LinkedBlockingDeque 長度為3 * 線程工程 Executors.defaultThreadFactory * 拒接策略 new ThreadPoolExecutor.AbortPolicy() 拋出異常 */ ExecutorService executorService = new ThreadPoolExecutor( 2, 5, 3, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy() ); int taskNum = 2; try{ for(int i = 1;i <= taskNum;i++){ for(int i = 1;i <= taskNum;i++){ final int tp = i; executorService.execute(()->{ System.out.println(Thread.currentThread().getName()+" ok,task" + tp); }); } } } finally { //線程池在不使用后 必須shutdowm executorService.shutdown(); } } }
下面通過修改任務數 taskNum 的值來查看執行結果
-
taskNum=2時
當前任務數和核心線程數一致,2個核心線程執行,阻塞隊列中沒有等待的任務
-
taskNum=5時
當前任務數超過了核心線程數,但是等待的任務數沒有超過阻塞隊列的長度,這時兩個線程執行,3個任務在隊列中等待,最終還是由兩個核心線程執行任務。
-
-
taskNum=8時
當前任務數超過了核心線程數和等待隊列的長度的和,這時線程池會再創建3個線程,此時線程數量到達最大線程數5。這時5個線程都執行。

-
taskNum>8時
當前的任務數已經超過了線程池所能容納的最大并發任務數(最大線程數+等待隊列長度),按照線程池創建時指定的拒絕策略,任務只執行到8,后面拋出異常。
四種拒絕策略
-
new ThreadPoolExecutor.AbortPolicy()
拋出異常
-
new ThreadPoolExecutor.CallerRunsPolicy()
由添加線程執行,task9被main線程執行
-
new ThreadPoolExecutor.DiscardPolicy()
丟掉任務,不拋出異常,任務只執行到8
-
new ThreadPoolExecutor.DiscardOldestPolicy()
嘗試競爭,不拋出異常
線程池最大值設置問題
分兩種情況
-
CPU密集型
這類任務的一個特點是非常占用cpu。所以在設置最大線程數時可以設置為CPU的核心數,這樣即可以保證效率,又不會浪費資源。
如果設置低了,并發處理任務的效率低。如果設置高了,線程上下文切換的開銷會影響到處理效率。
可以使用下列語句獲取cpu核心數
Runtime.getRuntime().availableProcessors()
-
IO密集型
這里任務的特點是,cpu在處理任務的過程中需要等待IO操作,比如讀寫文件,網絡IO等。
線程的上下文切換已經對任務處理效率的影響很小了。
這時可以把最大線程數設置得比cpu核心線程數大一些,比如兩倍。
原文鏈接:https://blog.csdn.net/wu1308156206/article/details/125688787
- 上一篇:CountDownLatch使用詳解
- 下一篇:Callable接口的使用詳解
相關推薦
- 2022-12-25 Redis中AOF與RDB持久化策略深入分析_Redis
- 2022-03-23 C#使用表達式樹實現對象復制的示例代碼_C#教程
- 2023-03-01 Python中getservbyport和getservbyname函數的用法大全_python
- 2022-05-13 C語言數據結構之二叉樹詳解_C 語言
- 2022-07-08 一文詳解C++中運算符的使用_C 語言
- 2021-12-31 element 級聯下拉菜單 獲取value 同時 獲取label
- 2022-06-13 Python+Matplotlib繪制雙y軸圖像的示例代碼_python
- 2022-08-16 C#在MEF框架中實現延遲加載部件_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支