1. 程式人生 > >Java多執行緒之鎖優化策略

Java多執行緒之鎖優化策略

轉載 http://www.cnblogs.com/ygj0930/p/6561264.html


編碼過程中可採取的鎖優化的思路有以下幾種:

  • 1:減少鎖持有時間

       例如:對一個方法加鎖,不如對方法中需要同步的幾行程式碼加鎖;
    
  • 2:減小鎖粒度

      例如:ConcurrentHashMap採取對segment加鎖而不是整個map加鎖,提高併發性;
    
  • 3:鎖分離

      根據同步操作的性質,把鎖劃分為的讀鎖和寫鎖,讀鎖之間不互斥,提高了併發性。
    
  • 4:鎖粗化

     這看起來與思路1有衝突,其實不然。思路1是針對一個執行緒中只有個別地方需要同步,所以把鎖加在同步的語句上而不是更大的範圍,減少執行緒持有鎖的時間;
    
     而鎖粗化是指:在一個間隔性地需要執行同步語句的執行緒中,如果在不連續的同步塊間頻繁加鎖解鎖是很耗效能的,因此把加鎖範圍擴大,把這些不連續的同步語句進行一次性加鎖解鎖。雖然執行緒持有鎖的時間增加了,但是總體來說是優化了的。
    
  • 5:鎖消除

      鎖消除是編譯器做的事:根據程式碼逃逸技術,如果判斷到一段程式碼中,堆上的資料不會逃逸出當前執行緒(即不會影響執行緒空間外的資料),那麼可以認為這段程式碼是執行緒安全的,不必要加鎖。
    

Java虛擬機器中採取的鎖優化策略:

  • 1:偏向鎖:鎖物件偏向於當前獲得它的執行緒,如果在接下來的沒有被其他執行緒請求,則持有該鎖的執行緒將不再需要進行同步操作(即:持有該鎖的執行緒在接下來的執行中遇到同步塊時不再需要lock和unlock了,直接執行即可)。當另一個執行緒申請該鎖時,當前執行緒的偏向模式才會結束,讓出該鎖。

  • 2:輕量級鎖:syncrhoized的底層實現是通過監視器monitor來控制的,而monitorenter與monitorexit這兩個原語是依賴作業系統互斥(mutex)來實現的。

互斥會導致執行緒掛起,並在較短的時間內又需要重新排程回原執行緒的,較為消耗資源。輕量級鎖(Lightweight Locking)利用了CPU原語Compare-And-Swap(CAS,彙編指令CMPXCHG),嘗試在進入互斥前,進行補救,減少多執行緒進入互斥的機率。

    如果偏向鎖失敗,那麼系統會進行輕量級鎖的操作,使用CAS操作來嘗試加鎖。如果輕量級鎖失敗,才呼叫系統級別的重量級鎖(syncrhoized)來加鎖。     
  • 3:自旋鎖:當執行緒申請鎖時,鎖被佔用,則讓當前執行緒執行一個忙迴圈(自旋),看看持有鎖的執行緒是否會很快釋放鎖。如果自旋後還沒獲得鎖,才進入同步阻塞狀態;

         3.1:自適應自旋:自旋的執行緒自旋的時間為同一個鎖上一次執行緒自旋並獲得鎖的耗時。如果對於這個鎖,自旋很少有成功的,就不自旋了,避免浪費CPU資源。
    

總體來說:

**** 為了儘量避免使用重量級鎖(作業系統層面的互斥),JVM首先會嘗試 輕量級鎖,輕量級鎖會嘗試使用CAS操作來獲得鎖,如果輕量級鎖獲得失敗,說明存在競爭。但是也許很快就能獲得鎖,就會嘗試 自旋鎖,將執行緒做幾個空迴圈,每次迴圈時都不斷嘗試獲得鎖。如果自旋鎖也失敗,那麼只能升級成 重量級鎖