萬惡的鎖

一直以來想寫一篇關於鎖的總結,由於java的鎖太多了,五花八門,而且有時候不知道怎麼去選擇,好多人都犯糊塗,固然本身也犯糊塗。本章並非介紹java中的concurrent包下的相關鎖,那個通常在開發中間件系統中經常使用,更多的人開發是在和數據庫打交道,接觸的都是數據庫級別的鎖。廢話很少說,直接開始。java

一、悲觀鎖redis

    悲觀鎖在數據庫中,通常都是這樣的select ... from xxx where id = xxx for update數據庫

    這種鎖,一上來就直接鎖住了一條語句,併發效率極其差,若是業務代碼處理稍微複雜一點基本上數據庫很快就掛了,會報一大堆cant get connect ,這種確定不會用的。安全

二、樂觀鎖併發

    這種鎖有不少公司都在用,好比說京東,通常來講分爲兩步。分佈式

    第一步,select ... from xxx where id = xxx高併發

    第二步,update xxx  set ... where id = xxx and xxx=....工具

    說白了就是先去撈取數據,而後帶上條件去更新。若是此時產生併發,那麼更新就會只有一個成功,其餘的都是失敗,也就是返回一個0。這時候我能夠選擇從新查詢繼續嘗試,或者直接返回異常。.net

三、分佈式鎖(redis)線程

    這個鎖其實通常都是用redis來作的,算是悲觀鎖的變種,可是好一點是鎖內存,只要共用一個redis集羣的系統均可以對其可見。也有不少公司喜歡用,好比說網易。通常來講也是分爲兩步。

    第一步,jedis.set(key, value, "NX", "EX", seconds)
    第二步,jedis.del(keys)

首先,先設置一個key,而後賦值value,而且設置超時時間。若是此時出現併發,那麼只要key沒有被刪掉,那麼只有一個線程會返回true,其餘的都返回false,這就能夠作相應的處理。可是必定要注意,要刪除jeids的key,這個好多人都會犯錯誤,我也犯過好屢次。我想此時,會有小夥伴說,加上finally啊,在finally裏面刪除。我一開始也是這麼想的,後來發現犯了一個很是嚴重的錯誤,由於redis的鎖畢竟不是真正的鎖,它任什麼時候候都是返回值。假設一個用戶進入鎖了,沒有釋放,另外一個用戶進來返回false,那也會執行finally塊,這樣的話鎖就沒意義了。因此這也是比較坑的地方,而且代碼風格很是很差,而且內存當作鎖很是不安全,可是相比1,2兩種鎖已經很不錯了。

四、支付寶的經常使用鎖

    在介紹支付寶的鎖以前先說一件事,我在加入支付寶以前,也對它的鎖感興趣,一直在想究竟支持如此高併發的場景的公司的鎖到底長什麼樣子。因此當我剛到公司就火燒眉毛的看源代碼,支付寶關於鎖的處理作成了一個工具類,很方便你們調用,裏面只用了不到50行代碼實現一個鎖。

    支付寶的鎖的核心是悲觀鎖!是的,你沒有聽錯,我看到之後很不解,我問了下咱們組一個特別吊的大牛---司令。我問他爲何要這樣實現?若是說用樂觀鎖不是更好麼?他問我你要是用樂觀鎖你怎麼處理?先查而後更新,而後不匹配你怎麼辦?我說就拋異常啊!他說,好的,那麼若是說你的業務比較複雜,執行到這一步花費了不少時間呢?你並非想當即拋異常,而是但願重試幾回呢?我說這個能夠用一個for循環,更新失敗就sleep,重試幾回。他說,能夠,那麼若是你用成千上萬個線程呢?這時候都在for,你的數據庫是否是掛了?我那時恍然大悟。那麼支付寶的鎖到底怎麼實現的呢?

@Transaction

b = false;

for(重試次數) {

    try {

    select .. from xx where id = xxx for update nowait;

    獲取鎖成功,b=true; 直接break;

    } catch() {

        異常捕獲;

    }

  sleep(ms)

}

if(!b) {

    throw new Exception(xxxx);

}

他給我解釋到:首先獲取鎖,由於有nowait關鍵字,那麼鎖會當即返回,若是沒有獲取到,在併發的時候會拋異常,而後證實搶鎖激烈那麼就sleep幾毫秒,而後重試搶鎖,若是搶到了就直接跳出循環。若是重試次數超過必定次數,那麼證實此時搶鎖特別激烈,那麼直接拋異常。

他說,咱們要知道數據庫的鏈接是很是寶貴的,在用完之後必定要及時回收,拋異常是最快的回收連接方式,因此,支付寶的代碼有不少的異常種類。雖然樂觀鎖看起來很清新,可是它長時間佔用着連接且在開發複雜業務有個弊端,每每寫代碼的人都沒法分辨何時該拋異常,致使將業務代碼處理的很亂,常常形成死鎖。這樣看來,帶nowait的悲觀鎖真的是很好,而且配合上事務也不用像redis那樣本身要刪除鎖,致使常常出錯,並且比較數據庫級別的鎖比內存穩定多了啊!

這就是本章介紹的幾種鎖,但願能給你們帶來些收穫!

相關文章
相關標籤/搜索