線程相關整理

先說阻塞隊列,即生產者消費者模式

舉個列子

在多線程下:所謂阻塞,在某些情況下會掛起線程(即阻塞),一旦條件滿足,被掛起的線程又會自動被喚醒。

爲什麼需要BlockingQueue?
好處是我們不需要關心什麼時候需要阻寒線程,什麼時候需要喚醒線程,因爲這一切BlockingQueue都給你辦了。在concurrent包發佈以前,在多線程環境下,我們每個程序員都必須去自己控制這些細節,尤其還要兼顧效率和線程安全,而這會給我們的程序帶來不小的複雜度。

線程池的底層就是阻塞隊列:

ArrayBlockingQueue:由數組結構組成的有界阻塞隊列。
LinkedBlockingQueue:由鏈表結構組成的有界(默認值爲Integer.MAX_VALUE)阻塞隊列。

PriorityBlockingQueue:支持優先級排序的無界塞隊列。
DelayQueue:使用優先級隊列實現的延遲無界阻塞隊列。
SynchronousQueue:不存儲元素的阻塞隊列,也即單個元素的隊列。
LinkedTransferQueue:由鏈表結構組成的無界阻塞隊列。
LinkedBlockingDeque:由鏈表結構組成的雙向阻塞隊列

說一下SynchronousQueue,與其他BlockingQueue不同,SynchronousQueue是一個不存儲元素的BlockingQueue(沒有容量)。
每一個put操作必須要等待一個take操作,否則不能繼續添加元素,反之亦然。

 

創建線程的幾種方式

1.繼承Thread類

2.實現Runable接口

3.實現Callable接口

Callable  FutureTask實現了Runable接口,構造方法傳入Callable(適配器模式)。面向接口的編程。

4.線程池…..

ThreadPoolExecutor  + 阻塞隊列

1、newCachedThreadPool:用來創建一個可以無限擴大的線程池,適用於負載較輕的場景,執行短期異步任務。(可以使得任務快速得到執行,因爲任務時間執行短,可以很快結束,也不會造成cpu過度切換)

2、newFixedThreadPool:創建一個固定大小的線程池,因爲採用無界的阻塞隊列,所以實際線程數量永遠不會變化,適用於負載較重的場景,對當前線程數量進行限制。(保證線程數可控,不會造成線程過多,導致系統負載更爲嚴重)

3、newSingleThreadExecutor:創建一個單線程的線程池,適用於需要保證順序執行各個任務。

4、newScheduledThreadPool:適用於執行延時或者週期性任務。

核心參數

1.corePoolSize:線程池中的常駐核心線程數
2.maximumPooISize:線程池能夠容納同時執行的最大線程數,此值必須大於等於1
3.keepAliveTime:多餘的空閒線程的存活時間。當前線程池數量超過corepooISize時,當空閒時間達到keepAeTime值時,多餘空閒線程會被銷燬直到只剩下corepooISize個線程爲止
4.unit:keepAliveTime的單位
5.workQueue:任務隊列,被提交但尚未被執行的任務
6.threadFactory:表示生成線程池中工作線程的線程工廠,用於創建線程一般用默認的即可
7.handIer:拒絕策略,表示當隊列滿了並且工作線程大於等於線程池的最大線程數(maximumPooISize)

底層原理

1. 在創建了線程池後,等待提交過來的任務請求。
2. 當調用execute()方法添加一個請求任務時,線程池會做如下判斷:
    2.1如果正在運行的線程數量小於corePoolSize,那麼直接創建線程運行這個任務;
    2.2如果正在運行的線程數量大於或等於corePoolSize,那麼將這個任務放入隊列;
    2.3如果這時候隊列滿了且正在運行的線程數量還小於maximumPoolSze,那麼還是要創建非核心線程立刻運行這個任務;
    2.4如果隊列滿了且正在運行的線程數量大於或等於maximumPoolSze,那麼線程池會啓動飽和拒絕略來執行。
3. 當一個線程完成任務時,它會從隊列中取下一個任務來執行。
4. 當一個線程無事可做超過一定的時間(keepAliveTime)時,線程池會判斷:|
    如果當前運行的線程數大於corePoolSize,那麼這個線程就被停掉。所以線程池的所有任務完成後它最終會收縮到corePoolStze的大小。

拒絕策略

AbortPolicy(默認):直拋出RejectedExecutionExcepton異常阻止系統正常運行。
CallerRunsPolicy:“調用者運行”一種調節機制,該策略既不會拋棄任務,也不會拋出異常,而是將某些任務回退給調用者運行。
DiscardOldestPolicy:拋棄隊列中等待最久的任務,然後把當前任務加入隊中嘗試再次提交當前任務。
DiscardPolicy:直接丟棄任務,不予任何處理也不拋出異常。如果允許任務丟失,這是最好的一種方案。

擴展:合理配置線程,你是如何考慮的?

 1.cpu密集型

CPU密集的意思是該任務需要大量的運算,而沒有阻塞,CPU一直全速運行。
CPU密集任務只有在真正的多核CPU上纔可能得到加速(通過多線程),而在單核CPU上,無論你開幾個模擬的多線程該任務都不可能得到加速,因爲CPU總的運算能力就那些。
因爲CPU密集型任務使得CPU使用率很高,若開過多的線程數,會造成CPU過度切換,所以CPU密集型任務配置儘可能少的線程數量:
一般公式:CPU核數+1個線程的線程池

2.io密集型

可以使用稍大的線程池,一般爲2*CPU核心數。 IO密集型任務CPU使用率並不高,因此可以讓CPU在等待IO的時候有其他線程去處理別的任務,充分利用CPU時間。

故需要多配置線程數:
參公式:CPU核數/1-阻塞係數    阻塞係數在0.8~0.9之間
比如8核CPU:8/1-0.9=80個線程數

線程池爲什麼要使用阻塞隊列?

1.因爲線程若是無限制的創建,可能會導致內存佔用過多而產生OOM,並且會造成cpu過度切換。

2.阻塞隊列可以保證任務隊列中沒有任務時阻塞獲取任務的線程,使得線程進入wait狀態,釋放cpu資源。當隊列中有任務時才喚醒對應線程從隊列中取出消息進行執行。使得在線程不至於一直佔用cpu資源。  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章