Tips
書中的源代碼地址:https://github.com/jbloch/effective-java-3e-source-code
注意,書中的有些代碼裏方法是基於Java 9 API中的,因此JDK 最好下載 JDK 9以上的版本。java
當許多線程能夠運行時,線程調度器(thread scheduler)決定哪些線程能夠運行以及運行多長時間。任何合理的操做系統都會嘗試公平地作出這個決定,可是策略可能會有所不一樣。所以,編寫良好的程序不該該依賴於此策略的細節。任何依賴線程調度器來保證正確性或性能的程序均可能是不可移植的。git
編寫健壯,響應迅速的可移植程序的最佳方法是確保可運行線程的平均數量不會明顯大於處理器數量。 這使得線程調度程序幾乎沒有多少選擇:它只是運行可運行的線程,直到它們再也不可運行爲止。 即便在徹底不一樣的線程調度策略下,程序的行爲也不會有太大變化。 請注意,可運行線程的數量與線程總數不一樣,後者可能要高得多。 正在等待的線程不可運行。github
保持可運行線程數量較少的主要技術是讓每一個線程作一些有用的工做,而後等待更多的工做。 若是線程沒有作有用的工做,它們就不該該運行。 就Executor Framework而言(條目 80),這意味着適當調整線程池的大小[Goetz06, 8.2],並保持任務簡短,但不要過短,不然分派的開銷會損害性能。多線程
線程不該該處於 busy-wait的狀態,反覆檢查等待其狀態改變的共享對象。 除了使程序容易受到線程調度程序的變化無常的影響以外,一直處於 busy-wait的狀態大大增長了處理器的負擔,減小了其餘人能夠完成的有用工做量。 做爲不應作的極端例子,請考慮CountDownLatch的這種不正當的從新實現:併發
// Awful CountDownLatch implementation - busy-waits incessantly! public class SlowCountDownLatch { private int count; public SlowCountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException(count + " < 0"); this.count = count; } public void await() { while (true) { synchronized(this) { if (count == 0) return; } } } public synchronized void countDown() { if (count != 0) count--; } }
在個人機器上,當1000個線程在鎖存器(latch)上等待時,SlowCountDownLatch比Java的CountDownLatch慢大約十倍。 雖然這個例子看起來有點牽強,可是看到系統中有一個或多個線程沒必要要地運行,這種狀況並不罕見。 性能和可移植性可能會受到影響。性能
當一個程序由於某些線程沒有得到足夠的CPU時間而沒法正常工做時,不要試圖經過調用Thread.yield
方法來「修復」這個程序。你可能會成功地使程序在某種程度上工做,但它不會是可移植的。在一個JVM實現上提升性能的相同的yield方法調用,在第二個JVM實現上可能會使性能變差,而在第三個JVM實現上沒有任何影響。Thread.yield
沒有可測試的語義。更好的作法是重構應用程序,以減小併發運行線程的數量。測試
相似警告適用的相關技術是調整線程優先級。 線程優先級是Java中最不可移植的功能之一。 經過調整一些線程優先級來調整應用程序的響應性並非不合理的,但它不多是必需的,而且不可移植。 嘗試經過調整線程優先級來解決嚴重的活躍度問題是不合理的。 在你找到並解決根本緣由以前,問題可能會從新出現。this
總之,不要依賴線程調度器來肯定程序的正確性。 由此產生的程序既不健壯也不可移植。 做爲推論,不要依賴Thread.yield
方法或線程優先級。 這些機制僅僅是對調度器的提示。 能夠謹慎地使用線程優先級來提升已經工做的程序的服務質量,可是它們永遠不該該用於「修復」幾乎不起做用的程序。操作系統