上一篇中分析了測試鎖的兩種實現TASLock和TTASLock,主要對這兩種鎖的性能進行了分析。對於TTASLock,咱們知道比TASLock性能上要好不少,具體分析已經講過了。咱們最後也說了,TTASLock雖然比TASLock大有改進,可是在性能上仍是不夠理想。這一篇的目的就是針對TTASLock作一下改進。 java
咱們再來看一下TTASLock的實現源碼和加鎖的流程圖: 緩存
/** * * Test test and set lock * */ public class TTASLock { private AtomicBoolean state = new AtomicBoolean(false); // 加鎖 public void lock() { while (true) { while (state.get()) { // 自旋 } if (!state.getAndSet(true)) { break; } } } // 解鎖 public void unlock() { state.set(false); } }加鎖流程圖以下:
從上文咱們知道,對於TTASLock鎖,性能問題主要出如今解鎖上。一旦一個已經得到鎖的線程執行解鎖操做。其餘線程都會產生緩存缺失,將會由」本地自旋」轉變爲從共享服務器中去獲取狀態值。這會消耗大量的總線資源。因此,若是咱們要對TTASLock改進的話,須要從這裏去想辦法。 服務器
這裏咱們就要一直成爲指數後退的技術。「指數後退」名字聽起來挺嚇人的,可是原理上很簡單,實現上也不是多複雜的事情。我舉個例子你們都明白了。咱們用迅雷帳戶登陸的時候,若是網絡斷掉了。迅雷會從新嘗試登陸。第一次嘗試多是5秒鐘之後。若是第一次嘗試失敗,第二次嘗試就會在10秒鐘之後登陸。依次類推,失敗的次數越多,嘗試登陸的延時時間就越長,成指數性「後退」。很簡單吧。 網絡
迅雷掉線「指數後退」方式的重登錄。 dom
這個和加鎖有什麼關係呢? 性能
咱們再來深刻看一下TTASLock的加鎖過程,一旦一個線程獲取不到鎖,它會一直的在本地自旋等待。若是有一百多個線程爭鎖,就有99個(有一個得到了鎖)在本地自旋等待。而那個得到鎖的線程一旦釋放鎖,這99個線程都會產生緩存缺失。一直總線風暴以後,仍是隻有一個線程能得到鎖。其餘的依然在本地不斷的自旋等待。過程分析完了,發現什麼問題了嗎?問題在於既然每次只有一個線程得到鎖,須要全部的線程同時自旋等待嗎?咱們難道不能讓等待線程「指數後退」嗎? 測試
OK,理論到此爲止。讓咱們按照這個思路來實現吧。既然指數後退,咱們先實現這部分功能。咱們用一個BackOff類來表明這個抽象。 .net
public class Backoff { // 須要對後退時間設置一個最大值和最小值 final int minDelay, maxDelay; int limit; final Random random; public Backoff(int min, int max) { maxDelay = max; minDelay = min; limit = minDelay; random = new Random(); } public void backoff() throws InterruptedException { // 計算須要睡眠的時間 int delay = random.nextInt(limit); // 從新計算limit,每次兩倍增長。但最大等於maxDelay limit = Math.min(maxDelay, 2 * limit); // 當前線程睡眠 Thread.sleep(delay); } }而後咱們把它用來改造咱們的TTASLock:
class BackoffLock { private AtomicBoolean state = new AtomicBoolean(false); private static final int MIN_DELAY = ...; private static final int MAX_DELAY = ...; // 加鎖 public void lock() throws InterruptedException { Backoff back = new Backoff(MIN_DELAY, MAX_DELAY); while (true) { while (state.get()) { // 自旋 } if (!state.getAndSet(true)) { return; } //關鍵點,一旦獲取鎖失敗,就指數後退 else { back.backoff(); } } } // 解鎖 public void unlock() { state.set(false); } }
上面實現和TTASLock相比就多了指數後退這一步操做。其餘的都沒有什麼變化。BackoffLock性能上比TAS系列要好的多。由於它不會在同時(隨機的妙用哦)存在大量的線程去爭鎖,不會形成總線風暴。BackoffLock最關鍵的地方是肯定睡眠的最小值和最大值,這個須要大量的測試才能肯定合適的值,並且這兩個值對性能影響很是大。因此BackoffLock鎖的可移植性很差,由於不一樣的機器對這兩個值都有不一樣的要求。 線程
正由於BackoffLock不是很是的完美,因此引出了咱們下一個主角的出現:隊列鎖。 code