java线程池的使用学习
目錄
- 1. 線程池的創(chuàng)建
- 2. 線程池的運(yùn)行規(guī)則
- 3. 線程池的關(guān)閉
- 4. 線程池的使用場(chǎng)合
- 5. 線程池大小的設(shè)置
- 6 實(shí)現(xiàn)舉例
1. 線程池的創(chuàng)建
線程池的創(chuàng)建使用ThreadPoolExecutor類(lèi),有利于編碼時(shí)更好的明確線程池運(yùn)行規(guī)則。
參數(shù)含義
(1) 核心線程數(shù)corePoolSize: 保持在池中的線程數(shù)
(2) 最大線程數(shù)maximumPoolSize
(3) 保活時(shí)間keepAliveTime: 線程數(shù)大于corePoolSize,閑置線程最大空閑時(shí)間
(4) 時(shí)間單位unit
(5) 阻塞隊(duì)列workQueue
java.util.concurrent.BlockingQueue主要實(shí)現(xiàn)類(lèi)有:
- ArrayBlockingQueue: 數(shù)組結(jié)構(gòu)有界阻塞隊(duì)列,FIFO排序。其構(gòu)造函數(shù)必須設(shè)置隊(duì)列長(zhǎng)度。
- LinkedBlockingQueue:鏈表結(jié)構(gòu)有界阻塞隊(duì)列,FIFO排序。隊(duì)列默認(rèn)最大長(zhǎng)度為Integer.MAX_VALUE,故可能會(huì)堆積大量請(qǐng)求,導(dǎo)致OOM。
- PriorityBlockingQueue:支持優(yōu)先級(jí)排序的無(wú)界阻塞隊(duì)列。默認(rèn)自然順序排列,可以通過(guò)比較器comparator指定排序規(guī)則。
- DelayQueue:支持延時(shí)獲取元素的無(wú)界阻塞隊(duì)列。隊(duì)列使用PriorityQueue實(shí)現(xiàn)。
(6) 線程創(chuàng)建接口threadFactory
- 默認(rèn)使用Executors.defaultThreadFactory()。
- 可以自定義ThreadFactory實(shí)現(xiàn)或使用第三方實(shí)現(xiàn),方便指定有意義的線程名稱(chēng)。
(7) 飽和策略handler
- ThreadPoolExecutor.AbortPolicy():終止策略(默認(rèn)) , 拋出java.util.concurrent.RejectedExecutionException異常。
- ThreadPoolExecutor.CallerRunsPolicy(): 重試添加當(dāng)前的任務(wù),他會(huì)自動(dòng)重復(fù)調(diào)用execute()方法。
- ThreadPoolExecutor.DiscardOldestPolicy(): 拋棄下一個(gè)即將被執(zhí)行的任務(wù),然后嘗試重新提交新的任務(wù)。最好不和優(yōu)先級(jí)隊(duì)列一起使用,因?yàn)樗鼤?huì)拋棄優(yōu)先級(jí)最高的任務(wù)。
- ThreadPoolExecutor.DiscardPolicy(): 拋棄策略, 拋棄當(dāng)前任務(wù)。
2. 線程池的運(yùn)行規(guī)則
execute添加任務(wù)到線程池:
一個(gè)任務(wù)通過(guò)execute(Runnable)方法被添加到線程池。任務(wù)是一個(gè) Runnable類(lèi)型的對(duì)象,任務(wù)的執(zhí)行方法就是 Runnable類(lèi)型對(duì)象的run()方法。
線程池運(yùn)行規(guī)則:
當(dāng)一個(gè)任務(wù)通過(guò)execute(Runnable)方法添加到線程池時(shí):
- 如果此時(shí)線程池中的數(shù)量小于corePoolSize,即使線程池中的線程都處于空閑狀態(tài),也要?jiǎng)?chuàng)建新的線程來(lái)處理被添加的任務(wù)。
- 如果此時(shí)線程池中的數(shù)量等于 corePoolSize,但是緩沖隊(duì)列 workQueue未滿,那么任務(wù)被放入緩沖隊(duì)列。
- 如果此時(shí)線程池中的數(shù)量大于corePoolSize,緩沖隊(duì)列workQueue滿,并且線程池中的數(shù)量小于maximumPoolSize,建新的線程來(lái)處理被添加的任務(wù)。
如果此時(shí)線程池中的數(shù)量大于corePoolSize,緩沖隊(duì)列workQueue滿,并且線程池中的數(shù)量等于maximumPoolSize,那么通過(guò) handler所指定的策略來(lái)處理此任務(wù)。
也就是:處理任務(wù)的優(yōu)先級(jí)為:
核心線程corePoolSize - > 任務(wù)隊(duì)列workQueue - > 最大線程maximumPoolSize
如果三者都滿了,使用handler策略處理該任務(wù)。當(dāng)線程池中的線程數(shù)量大于 corePoolSize時(shí),如果某線程空閑時(shí)間超過(guò)keepAliveTime,線程將被終止。這樣,線程池可以動(dòng)態(tài)的調(diào)整池中的線程數(shù)。
3. 線程池的關(guān)閉
通過(guò)調(diào)用線程池的shutdown或shutdownNow方法來(lái)關(guān)閉線程池。
- shutdown:將線程池的狀態(tài)設(shè)置成SHUTDOWN狀態(tài),然后interrupt空閑線程。
- shutdownNow:線程池的狀態(tài)設(shè)置成STOP,然后嘗試interrupt所有線程,包括正在運(yùn)行的。
關(guān)于線程池狀態(tài),源碼中的注釋比較清晰:
再看一下源代碼:
// 在關(guān)閉中,之前提交的任務(wù)會(huì)被執(zhí)行(包含正在執(zhí)行的,在阻塞隊(duì)列中的),但新任務(wù)會(huì)被拒絕。public void shutdown() {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {checkShutdownAccess();// 狀態(tài)設(shè)置為shutdownadvanceRunState(SHUTDOWN);// interrupt空閑線程interruptIdleWorkers();onShutdown(); // hook for ScheduledThreadPoolExecutor} finally {mainLock.unlock();}// 嘗試終止線程池tryTerminate();}其中,interruptIdleWorkers()方法往下調(diào)用了interruptIdleWorkers(), 這里w.tryLock()比較關(guān)鍵。
中斷之前需要先tryLock()獲取worker鎖,正在運(yùn)行的worker tryLock()失敗(runWorker()方法會(huì)先對(duì)worker上鎖),故正在運(yùn)行的worker不能中斷。
4. 線程池的使用場(chǎng)合
(1)單個(gè)任務(wù)處理的時(shí)間比較短;
(2)需要處理的任務(wù)數(shù)量大;
5. 線程池大小的設(shè)置
可根據(jù)計(jì)算任務(wù)類(lèi)型估算線程池設(shè)置大小:
cpu密集型:可采用Runtime.avaliableProcesses()+1個(gè)線程;
IO密集型:由于阻塞操作多,可使用更多的線程,如2倍cpu核數(shù)。
6 實(shí)現(xiàn)舉例
場(chǎng)景: ftp服務(wù)器收到文件后,觸發(fā)相關(guān)搬移/處理操作。
public class FtpEventHandler extends DefaultFtplet {@Overridepublic FtpletResult onUploadEnd(FtpSession session, FtpRequest request)throws FtpException, IOException {// 獲取文件名String fileName = request.getArgument();Integer index = fileName.lastIndexOf("/");String realFileName = fileName.substring(index + 1);index = realFileName.lastIndexOf("\\");realFileName = realFileName.substring(index + 1);// **處理文件**ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 50, 10,TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3));threadPool.execute(new fileSenderThread(realFileName));return FtpletResult.DEFAULT;} }Spring也提供了ThreadPoolTaskExecutor
<!--spring.xml配置示例--><bean id="gkTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"><property name="allowCoreThreadTimeOut" value="true"/><property name="corePoolSize" value="10"/><property name="maxPoolSize" value="50"/><property name="queueCapacity" value="3"/><property name="keepAliveSeconds" value="10"/><property name="rejectedExecutionHandler"value="#{new java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy()}"/><property name="threadNamePrefix" value="gkTaskExecutor"/></bean>//java代碼中注入bean@Autowired @Qualifier("gkTaskExecutor") private ThreadPoolTaskExecutor gkTaskExecutor;end.
轉(zhuǎn)載于:https://www.cnblogs.com/eaglediao/p/7742570.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專(zhuān)家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的java线程池的使用学习的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 某商超虚拟化方案
- 下一篇: [face_recognition中文文