線程每次創建和關閉的開銷非常大,我們可以使用線程池來管理我們的線程,可以充分利用線程,減少不必要的開銷。
創建線程的方式有三種:
1、繼承Thread類
2、實現Runable或者Callable(帶返回值)接口
3、線程池的方式啟動
今天我們主要針對線程池來進行展開討論:
JDK自帶創建線程的方式有多種:
這里是針對JDK1.8版本。每個創建都不一樣,但是使用JDK自帶的線程池會出現OOM問題,中小型公司一般狠難遇到,在阿里巴巴開發文檔上面有明確的標識:
如果有需要此文檔,請點擊下面的url進行下載:https://www.cnblogs.com/han-1034683568/p/7680354.html
既然JDK自帶的線程池我們不能用,那么我們要自己手動來寫線程池了, 如果創建一個線程池:
下面的是對應的七個參數:
ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
我們可以這樣來創建線程池,其他里面的參數表示什么意思,我們在這里說明一下啊:
corePoolSize:線程中常駐核心線程數
maximumPoolSize:線程池能夠容納同時執行的最大線程數,此值必須大於1
keepAliveTime:多余空閑線程的存活時間,
unit:keepAliveTime的單位
workQueue:任務隊列,被提交但尚未被執行的任務
threadFactory:表示生成線程池中工作線程的線程工廠,用於創建線程,一般的默認即可
handler:拒絕策略,表示當隊列滿了,並且工作線程大於等於線程池的最大線程數(maximumPoolSize)
總體步驟:
1、在創建線程后,等待提交過來的任務請求
2、當調用execute()方法添加一個請求任務的時候,線程池會如何判斷
2.1、如果正在運行的線程數小於corePoolSize,那么馬上創建線程運行這個認為
2.2、如果正在運行的線程數大於等於corePoolSize,那么將這個認為放入隊列
2.3、如果這個時候隊列滿了,且小於maximumPoolSize,那么還是要創建線程立即執行任務
2.4、如果隊列滿了,maximumPoolSize也滿了,將會啟動飽和拒絕策略來執行
3、將一個線程完成任務的時候,他會沖=從隊列里面取下一個任務來執行
4、當一個線程無事的時候,超過一定時間的時候,線程池會判斷:如果當前運行的線程數大於等於corePoolSize,那么這個線程將被停掉,所以線程池的所有任務完成后它最終收縮到corePoolSize的大小
線程池的拒絕策略:
ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。(默認) ThreadPoolExecutor.DiscardPolicy:丟棄任務,但是不拋出異常。 ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然后重新提交被拒絕的任務 ThreadPoolExecutor.CallerRunsPolicy:由調用線程(提交任務的線程)處理該任務
添加阻塞隊列的方式有四種:
1、add,添加不進去的時候,就會拋異常
2、offer,添加進去返回true,添加不進去返回false
3、put,會一直阻塞,直到可以放進去為止
4、offer+時間,如果阻塞隊列滿的時候,往阻塞隊列添加,多久沒有添加進去,則停止添加
如何合理的配置線程池:
這看你的業務了,看你的業務是CPU密集還是IO密集
如果是CPU密集,一般配置的是CPU的核數或者核數+1
如果是IO密集,大部分的線程都會阻塞,則需要多配置線程數,公式(CPU/(1-阻塞系數)),阻塞系數在0.8-0.9之間,比如8核CPU:8/(1-0.9) = 80個線程