volatile只能強調數據的可見性,並不能保證原子操做和線程安全,所以volatile不是萬能的。參考指令重排序html
volatile最多見於下面兩種場景。java
a. 循環檢測機制安全
b. 單例模型 (http://www.blogjava.net/xylz/archive/2009/12/18/306622.html)
網絡
看起來Lock有更好的性能以及更靈活的控制,是否徹底能夠替換synchronized?多線程
在鎖的一些其它問題中說過,synchronized的性能隨着JDK版本的升級會愈來愈高,而Lock優化的空間受限於CPU的性能,頗有限。另外JDK內部的工具(線程轉儲)對synchronized是有一些支持的(方便發現死鎖等),而對Lock是沒有任何支持的。併發
也就說簡單的邏輯使用synchronized徹底沒有問題,隨着機器的性能的提升,這點開銷是能夠忽略的。並且從代碼結構上講是更簡單的。簡單就是美。函數
對於複雜的邏輯,若是涉及到讀寫鎖、條件變量、更高的吞吐量以及更靈活、動態的用法,那麼就能夠考慮使用Lock。固然這裏尤爲須要注意Lock的正確用法。工具
必定要將Lock的釋放放入finally塊中,不然一旦發生異常或者邏輯跳轉,頗有可能會致使鎖沒有釋放,從而發生死鎖。並且這種死鎖是難以排查的。性能
若是須要synchronized沒法作到的嘗試鎖機制,或者說擔憂發生死鎖沒法自恢復,那麼使用tryLock()是一個比較明智的選擇的。優化
甚至可使用獲取鎖一段時間內超時的機制Lock.tryLock(long,TimeUnit)。 鎖的使用能夠參考前面文章的描述和建議。
一個流行的錯誤是這樣的。
看起來很合理的,對於一個線程安全的Map實現,要存取一個不重複的結果,先檢測是否存在而後加入。 其實咱們知道兩個原子操做和在一塊兒的指令序列不表明就是線程安全的。 割裂的多個原子操做放在一塊兒在多線程的狀況下就有可能發生錯誤。
實際上ConcurrentMap提供了putIfAbsent(K, V)的「原子操做」機制,這等價於下面的邏輯:
除了putIfAbsent還有replace(K, V)以及replace(K, V, V)兩種機制來完成組合的操做。
提到Map,這裏有一篇談HashMap讀寫併發的問題。
下面的實例是在構造函數中啓動一個線程。
這裏可能存在的陷阱是若是此類被繼承,那麼啓動的線程可能沒法正確讀取子類的初始化操做。
所以一個簡單的原則是,禁止在構造函數中啓動線程,能夠考慮可是提供一個方法來啓動線程。若是非要這麼作,最好將類設置爲final,禁止繼承。
這篇文章裏面提到過notify丟失通知的問題。
對於wait/notify/notifyAll以及await/singal/singalAll,若是不肯定究竟是否可以正確的收到消息,擔憂丟失通知,簡單一點就是老是通知全部。
若是擔憂只收到一次消息,使用循環一直監聽是不錯的選擇。
很是主用性能的系統,可能就須要區分究竟是通知單個仍是通知全部的掛起者。
並非線程數越多越好,在下一篇文章裏面會具體瞭解下性能和可伸縮性。 簡單的說,線程數多少沒有一個固定的結論,受限於CPU的內核數,IO的性能以及依賴的服務等等。所以選擇一個合適的線程數有助於提升吞吐量。
對於CPU密集型應用,線程數和CPU的內核數一致有助於提升吞吐量,全部CPU都很繁忙,效率就很高。 對於IO密集型應用,線程數受限於IO的性能,某些時候單線程可能比多線程效率更高。但一般狀況下適當提升線程數,有利於提升網絡IO的效率,由於咱們老是認爲網絡IO的效率比較低。
對於線程池而言,選擇合適的線程數以及任務隊列是提升線程池效率的手段。
對於線程池來講,若是任務老是有積壓,那麼能夠適當提升corePoolSize大小;若是機器負載較低,那麼能夠適當提升maximumPoolSize的大小;任務隊列不長的狀況下減少keepAliveTime的時間有助於下降負載;另外任務隊列的長度以及任務隊列的拒絕策略也會對任務的處理有一些影響。