分布式鎖的簡單實現

2021-07-03 04:45:09 字數 4355 閱讀 8028

分布式鎖在分布式應用當中是要經常用到的,主要是解決分布式資源訪問衝突的問題。  一開始考慮採用reentrantlock來實現,但是實際上去實現的時候,是有問題的,reentrantlock的lock和unlock要求必須是在同一執行緒進行,而分布式應用中,lock和unlock是兩次不相關的請求,因此肯定不是同一執行緒,因此導致無法使用reentrantlock。

接下來就考慮採用自己做個狀態來進行鎖狀態的記錄,結果發現總是死鎖,仔細一看**,能不鎖死麼。 12

3456

78910

1112

public synchronized void lock()

lock=true;

...}

public synchronized void unlock()

第乙個請求要求獲得鎖,好麼,給他個鎖定狀態,然後他拿著鎖去幹活了。

這個時候,第二個請求也要求鎖,ok,他在lock中等待解鎖。

第乙個幹完活了,過來還鎖了,這個時候悲催了,因為,他進不了unlock方法了。

可能有人會問,為什麼採用while,而不是採用wait...notify?這個問題留一下,看看有人能給出來不?

總之,上面的方安案流產了。

同樣,不把synchronized 放在方法上,直接放在方法裡放個同步物件可以不??道理是一樣的,也會發生上面一樣的死鎖。

到此為止前途一片黑暗。

@沈學良

同學的寫了乙個用zk做的同布鎖,感覺還是比較複雜的且存疑。自己做不出來吧,又不死心。

再來看看lock的介面,想了一下,不遵守lock的介面了。編寫了下面的介面。 12

3456

789public inte***ce distributedlock extends remoteobject

呵呵,眼尖的同學可能已經發現不同了。

lock方法增加了個long返回值,trylock方法,返回的也不是boolean,也是long,unlock方法多了乙個long引數型引數,呵呵,技巧就在這裡了。 12

3456

78910

1112

1314

1516

1718

1920

2122

2324

2526

2728

2930

3132

3334

3536

3738

3940

4142

4344

4546

4748

4950

5152

5354

5556

5758

5960

6162

6364

6566

6768

6970

7172

7374

7576

public class distributedlockimpl extends unicastremoteobject implements distributedlock

/*** @param locktimeout 鎖超時時間,如果加鎖的物件不解鎖,超時之後自動解鎖

* @param locktimeoutunit

* @throws remoteexception

*/public distributedlockimpl(long locktimeout, timeunit locktimeoutunit) throws remoteexception

public long lock() throws timeoutexception

private boolean islocktimeout()

return (system.currenttimemillis() - beginlocktime) < locktimeoutunit.tomillis(locktimeout);

}private long gettoken()

public long trylock(long time, timeunit unit) throws timeoutexception

}thread.sleep(1);

} catch (interruptedexception e)

}return gettoken();}}

public void unlock(long token) else }}

下面對**進行一下講解。

上面的**提供了,永遠等待的獲取鎖的lock方法和如果在指定的時間獲取鎖失敗就獲得超時異常的trylock方法,另外還有乙個unlock方法。

技術的關鍵點實際上就是在token上,上面的實現,有乙個基本的假設,就是兩次遠端呼叫之間的時間不可能在1納秒之內完成。因此,每次鎖的操作都會返回乙個長整型的令牌,就是當時執行時間的納秒數。下次解鎖必須用獲得的令牌進行解鎖,才可以成功。如此,解鎖就不用新增同步操作了,從而解決掉上面死鎖的問題。

實際上,沒有令牌也是可以的,但是那樣就會導致a獲取了鎖,但是b執行unlock也會成功解鎖,是不安全的,而加入令牌,就可以保證只有加鎖者才可以解鎖。

下面是測試**: 12

3456

78910

1112

1314

1516

1718

1920

2122

2324

2526

2728

2930

3132

3334

3536

37public class testdlock

long s = system.currenttimemillis();

processor.start();

long e = system.currenttimemillis();

system.out.println(e - s);

rmiserver.unexportobject(distributedlock);}}

class runlock extends abstractprocessor

@override

protected void action() throws exception

system.out.println("end-" + thread.currentthread().getid());

} catch (remoteexception e) }}

運**況:12

3456

78910

1112

1314

1516

1718

1920

2122

2324

2526

27-0    [main] info   - 執行緒組執行開始,執行緒數8...

-3    [aa-aa0] info   - 執行緒執行開始...

-3    [aa-aa1] info   - 執行緒執行開始...

-3    [aa-aa2] info   - 執行緒執行開始...

-3    [aa-aa3] info   - 執行緒執行開始...

-3    [aa-aa4] info   - 執行緒執行開始...

-4    [aa-aa5] info   - 執行緒執行開始...

-4    [aa-aa6] info   - 執行緒執行開始...

-8    [aa-aa7] info   - 執行緒執行開始...

end-19

-9050 [aa-aa3] info   - 執行緒執行結束

end-17

-9052 [aa-aa1] info   - 執行緒執行結束

end-20

-9056 [aa-aa4] info   - 執行緒執行結束

end-16

-9058 [aa-aa0] info   - 執行緒執行結束

end-21

-9059 [aa-aa5] info   - 執行緒執行結束

end-26

-9063 [aa-aa7] info   - 執行緒執行結束

end-18

-9064 [aa-aa2] info   - 執行緒執行結束

end-22

-9065 [aa-aa6] info   - 執行緒執行結束

-9066 [main] info   - 執行緒組執行結束, 用時:9065ms

9069

也就是9069

ms中執行了8000次鎖定及解鎖操作。

小結:

上面的分布式鎖實現方案,綜合考慮了實現簡單,鎖安全,鎖超時等因素。實際測試,大概900到1000次獲取鎖和釋放鎖操作每秒,可以滿足大多數應用要求。

由於昨天有粉要求盡快發出,因此發得比較匆忙,不足之處及bug在所難免,歡迎拍磚。

分布式鎖簡單實現

基於zookeeper的實現方式 基於redis的實現方式 基於資料庫實現分布式鎖 基於快取,實現分布式鎖,如redis 基於zookeeper實現分布式鎖 樂觀鎖機制其實就是在資料庫表中引入乙個版本號 version 欄位來實現的。當我們要從資料庫中讀取資料的時候,同時把這個version欄位也讀...

Redis分布式鎖簡單實現

偽 下訂單 1 查庫存 getstock 2 判斷庫存 stock 0下單 3 下單 addorder 4 減庫存 public class redisutils setnx param key param value param seconds 過期時間,單位秒 return public sta...

簡單聊聊分布式鎖 Redis分布式鎖

單機redis分布式鎖 單機redis分布式鎖 首先咱們先聊聊單機的redis分布式鎖 第乙個最普通的實現方式,就是在 redis 裡使用 setnx 命令建立乙個 key,這樣就算加鎖。set resource name my random value nx px 30000執行這個命令就 ok。...