轉載請注明原文地址: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操作來獲得鎖,如果輕量級鎖獲得失敗,說明存在競爭。但是也許很快就能獲得鎖,就會嘗試自旋鎖,將線程做幾個空循環,每次循環時都不斷嘗試獲得鎖。如果自旋鎖也失敗,那么只能升級成重量級鎖。