第七十二條 不要依賴於執行緒排程器
當有多個執行緒可以執行時,由執行緒排程器決定哪些執行緒將會執行,以及執行多長時間。任何一個合理的作業系統在做出這樣的決定時,都會努力做到公正,但是所採用的策略卻相差無幾。因此,編寫良好的程式不應該依賴於這種策略的細節。任何依賴於執行緒排程器來達到正確性或者效能要求的程式,很有可能都是不可移植的。我們一般是需要把功能單獨模組化,保持獨立,超級解耦,隨時都能被拎出來,被其他專案複用。
要編寫健壯的、響應良好的、可移植的多執行緒應用程式,最好的辦法是確保可執行執行緒的平均數量稍微小於多餘處理器的數量。這使得執行緒排程器沒有更多的選擇:它只需要執行這些可執行的執行緒,直到他們不再可執行為止。即使在根本不同的執行緒排程演算法下,這些程式的行為也不會有很大的變化。注意可執行執行緒的數量並不等於執行緒的總數量,在等待的執行緒並不是可執行的,處理完一條執行執行緒,有空額餘出後,馬上讓一條等待執行緒變為執行執行緒,依次迴圈。
保持可執行執行緒數量儘可能少的主要方法是,讓每個執行緒做些有意義的工作,如果執行緒沒有在做有意義的工作,就不應該執行。根據Executor Framework,這意味著適當地規定了執行緒池的大小,並且使任務保持適當地小,彼此獨立,任務處理資料的量不能太小,否則沒必要單獨開執行緒,可以適當的把資料合併處理,否則分配的開銷也會影響到效能;執行緒不能一直出於等待的狀態,也不能反覆的檢查一個物件,直到達到某種臨界值,這樣會極大影響效率。
CountDownLatch 是通過阻塞當前執行緒,執行緒會進入了一個死迴圈當中,在這個死迴圈裡面,會不斷的進行判斷值,但這個死迴圈並不是一刻不停的,而是到後面會執行LockSupport.park(this);,來禁用當前執行緒的。如果我們想自定義一個阻塞執行緒,單純的用java中的for迴圈不停的去檢查物件,可以想象,一秒中for迴圈會執行多少遍,這會嚴重消耗效能,得不償失。系統有現成的,我們就沒必要再去造輪子了。
執行緒的優先順序是由等級決定的,如果想讓某條執行緒儘量的先執行,一個是提高執行緒的等級,另外一個就是儘量早的執行執行緒。使用執行緒池的話,如果核心執行緒和等待執行緒不夠用的話,我們應儘量調整執行緒池的策略,而不是一味的開執行緒。可以通過合併、延遲等方法來達到目的。我們應該遵循邏輯的正確性,而不是完全依賴執行緒排程器。不要依賴於 Thread.yield 來進行執行緒優先。