併發編程的最主要目的是提升程序的運行性能,線程可使程序更加充分的利用系統的可用處理能力,從而提升系統的資源利用率。然而使用多線程時也會引入額外的開銷,這些開銷包括:線程之間的協調(加鎖,內存同步等)、增長的的上下文切換、線程的建立和銷燬和線程的調度等等。若是過分以及不恰當的使用線程,這些開銷甚至會抵消因爲提升吞吐量、計算能力帶來的性能提高。一個設計糟糕的多線程程序其性能可能比相同功能的串行程序效率還低。java
提高性能意味着用更少的資源作更多的事情。資源的範圍很廣,好比CPU,內存,網絡帶寬,IO,磁盤空間等等。當操做性能因爲某種特定的資源而受到限制時,咱們一般稱爲資源密集型操做,好比CPU密集型等等。編程
要想經過併發來得到良好的性能,須要努力作好兩件事:要有效的利用現有的資源,以及新的處理資源出現時程序可以有效的利用新增的資源。從性能監視的視角來看,CPU須要儘量保持忙碌狀態(固然這不意味着把CPU用在一些無用的計算上)。安全
應用程序的性能能夠經過多個指標來衡量,好比一些指標(服務時間,等待時間)用來衡量運行速度,另外一些指標(生產量、吞吐量)來衡量處理能力。所以,「多快」、「多少」是性能優化的兩個不一樣方向,它們相互獨立,有時甚至是互相矛盾的。所以,在優化以前必需要先確保如下幾點:性能優化
第一,首先使程序正確,而後再提升運行速度,避免不成熟的優化。有些同步加鎖的方法看上去彷佛能夠被一些「聰明」的減小同步的方法所取代,但這每每會引起併發錯誤,併發錯誤是最難追蹤和消除的,這無疑於填小坑,挖大坑。網絡
第二,肯定目標,更快的含義是什麼?在什麼條件下運行更快?是在高負載仍是低負載?是大數據量仍是小數據量?對目前的情況以及目標情況都要有清晰地認識;多線程
第三,以測試爲基準,不要猜想。併發
在有些問題中,可用資源越多,那麼問題的解決速度越快,好比收割莊稼,人越多則收割越快,有些任務是串行的,即便增長資源也不能提升速度。程序也是同樣的,它由一些串行的和並行的程序組合而成,Amdahl定律描述的是:在增長計算資源的狀況下,程序在理論上可以實現最高加速比,這個值取決於程序中並行組件與串行組件所佔的比重。用公式表示爲:性能
Speedup <= 1/(F+(1-F)/N)測試
Speedup 表示最高加速比,F表示程序中必須被串行的部分,N表示程序處理器的數量。當N趨向於無窮大時,最高的加速比傾向於1/F。若是程序中的串行程序佔比10%,那麼最高的加速比接近10。大數據
要想算出程序中串行執行的比例其實很困難。可是Amdahl定律的出現卻給咱們提供了一些優化的思路。好比鎖分段。根據Amdahl定律,隨着處理器數量的增長,鎖分段的效率要比獨佔鎖和鎖分解的效率高,也更能充分利用多處理器的能力。ConcurrentHashMap之因此有較高的性能就是使用了鎖分段技術。
咱們知道,串行程序會下降程序的可伸縮性,而線程的上下文切換也會下降性能,而在鎖上等待會同時致使以上兩種問題。所以,減小鎖的競爭可提升程序的伸縮性和性能。有兩個因素將影響在鎖上發生競爭的可能性:鎖得請求頻率和每次持有鎖的時間。二者的乘積越小則鎖的競爭越小。如下將介紹幾種減小鎖競爭的方法:
第一,減小鎖持有的時間(快進快出)。縮小鎖的範圍,只在須要鎖操做的程序上使用鎖,將鎖無關的操做移出同步代碼塊。
第二,下降鎖的請求頻率。減少鎖的粒度,這能夠經過鎖分解和鎖分段技術實現。
第三,使用帶有協調機制的獨佔鎖,這些機制容許更高的併發性。好比咱們能夠用現有的併發容器替換掉同步容器以及使用原子類和讀寫鎖。由於java自帶的併發容器已經實現了很好的協調機制,咱們能夠直接使用提升效率。
最後要說的是性能優化是一個持續的、無止境的過程。所以,在實際優化過程當中要有明確的目標,在確保程序安全性和正確性的基礎上按部就班的去展開,避免盲目和過分優化。