1. 程式人生 > >Java並發編程實踐讀書筆記(5) 線程池的使用

Java並發編程實踐讀書筆記(5) 線程池的使用

設計 java並發編程 指定 數據交換 什麽 讀書 body 發展 ima

Executor與Task的耦合性

1,除非線程池很非常大,否則一個Task不要依賴同一個線程服務中的另外一個Task,因為這樣容易造成死鎖;

2,線程的執行是並行的,所以在設計Task的時候要考慮到線程安全問題。如果你認為只會在單任務線程的Executor中運行的話,從設計上講這就已經耦合了。

3,長時間的任務有可能會影響到其他任務的執行效率,可以讓其他線程在等待的時候限定一下等待時間。不要無限制地等待下去。

確定線程池的大小

給出如下定義:

技術分享圖片

要使CPU達到期望的使用率,線程池的大小應設置為:

技術分享圖片

約等於Ncpu+1。

定制線程池

通過Executor來創建的線程池其實是預先給我們打造好的線程池,例如:

技術分享圖片

如果不能滿足需求,可以自己量身定做:

    public ThreadPoolExecutor(int corePoolSize, //基本大小,沒有執行任務時線程池的大小
                              int maximumPoolSize,  //最大任務數量
                              long keepAliveTime, //最大空閑時間,超時後會被回收
                              TimeUnit unit,
                              BlockingQueue
<Runnable> workQueue) { //... }

任務隊列:緩存任務

前面有說到,使用線程池的好處是可以限制線程的數量,因此保證服務器不會被超量的線程給拖垮。當並發請求增加,我們不再是盲目地創建線程了。而是簡單地創建一個任務緩存到處理隊列中了。那麽問題來了,當並發請求繼續激增,服務線程無法消化任務,造成大量的任務堆積到隊列中。這也是會消耗服務器資源的。

我們需要讓服務器實在忙不過來的時候,扔掉一些請求。以保證自己不會奔潰。該放手的時候一定要放手,不是每個人都能隨隨便便造一個航天飛船發上天啊。服務器真的應付不過來的時候最重要的是要保護好自己。

創建線程池的時候,可以自己指定任務隊列:

無界隊列:就是創建隊列的時候不設置大小;

有界隊列:通上設置一個大小;

同步交換隊列:SynchronousQueue,嚴格意義上說,它並不是真的隊列,它只是一種數據交換的工具。任務來之後,這個隊列直接把任務交給線程池中的一個線程。如果沒有可用線程,則創建一個線程。如果不能創建了會拒絕這個任務。同步交換隊列的核心就是它不緩存任務,要麽線程池足夠大,要麽就直接拒絕。用於線程池可以無限增長或不會出現任務激增的場景。

可以用PriorityBlockingQueue來實現任務的排序。

飽和策略:任務隊列中都存不了了怎麽辦

線程池提供了多種處理辦法:

技術分享圖片

Abort:終止,對外拋RejectedExecutionException;

Discard:悄悄拋棄,註意是悄悄地,客戶端根本不會知道自己提交的任務根本就沒運行;

DiscardOldest:拋棄最老的,如果使用的是優先隊列,被拋棄的會是優先級最高的那個(所以DiscardOldest不要和優先隊列搭配使用)。

Caller-Runs:用客戶端線程來直接運行任務代碼。就是哪個線程在往線程池扔任務,那麽就用這個線程來直接跑這個任務。這麽做的好處是,讓客戶端分擔線程池的負擔,更主要的是讓客戶端先忙一會兒別的,從而暫停往線程池提交任務的動作。以期望線程池能最終緩過來。客戶端都來幫著線程池做事了,那它自己的事肯定也就沒人做了。如果客戶端代碼是一個Web請求處理程序,那麽它將不再accpet新的請求,這些新的請求會被堵在TCP的緩存中,如果系統一直沒有緩過勁來,再繼續發展下去,就會把“過載”的信息彌漫到TCP的調用端了。整個這套設計的潛臺詞是“我們已經盡力了”。系統不會硬著陸直接掛掉,如果請求壓力放緩,系統還是可以扛過來的。

線程工廠:定制線程

可以自己實現線程工廠來構建線程變量,從而定義更多需要的信息。

擴展ThreadPoolExecutor

ThreadPoolExecutor有一些生命周期的方法:beforeExecute、afterExecute和terminated。可以重寫這些方法,實現統計和監控的功能。

遞歸算法的並行化

什麽樣的情況可以並行?

比如for循環去做一些事情,而這些事情是相互獨立的。那麽這些事情其實可以並行來做的(for循環是串行執行)。

這給我們的編程提供了新的思路,很多寫了很多遍、平淡無奇的代碼其實有更有趣的實現方式。

Java並發編程實踐讀書筆記(5) 線程池的使用