redis分布式鎖-SETNX實現

轉自:https://my.oschina.net/u/1995545/blog/366381

Redis有一系列的命令,特點是以NX結尾,NX是Not eXists的縮寫,如SETNX命令就應該理解為:SET if Not eXists。這系列的命令非常有用,這里講使用SETNX來實現分布式鎖。?

用SETNX實現分布式鎖?
利用SETNX非常簡單地實現分布式鎖。例如:某客戶端要獲得一個名字foo的鎖,客戶端使用下面的命令進行獲取:?
SETNX lock.foo <current Unix time + lock timeout + 1>?

  • 如返回1,則該客戶端獲得鎖,把lock.foo的鍵值設置為時間值表示該鍵已被鎖定,該客戶端最后可以通過DEL lock.foo來釋放該鎖。

  • 如返回0,表明該鎖已被其他客戶端取得,這時我們可以先返回或進行重試等對方完成或等待鎖超時。



解決死鎖?
上面的鎖定邏輯有一個問題:如果一個持有鎖的客戶端失敗或崩潰了不能釋放鎖,該怎么解決?我們可以通過鎖的鍵對應的時間戳來判斷這種情況是否發生了,如果當前的時間已經大于lock.foo的值,說明該鎖已失效,可以被重新使用。?

發生這種情況時,可不能簡單的通過DEL來刪除鎖,然后再SETNX一次,當多個客戶端檢測到鎖超時后都會嘗試去釋放它,這里就可能出現一個競態條件,讓我們模擬一下這個場景:?

C0操作超時了,但它還持有著鎖,C1和C2讀取lock.foo檢查時間戳,先后發現超時了。?
C1 發送DEL lock.foo?
C1 發送SETNX lock.foo 并且成功了。?
C2 發送DEL lock.foo?
C2 發送SETNX lock.foo 并且成功了。?
這樣一來,C1,C2都拿到了鎖!問題大了!?

幸好這種問題是可以避免的,讓我們來看看C3這個客戶端是怎樣做的:?

C3發送SETNX lock.foo 想要獲得鎖,由于C0還持有鎖,所以Redis返回給C3一個0?
C3發送GET lock.foo 以檢查鎖是否超時了,如果沒超時,則等待或重試。?
反之,如果已超時,C3通過下面的操作來嘗試獲得鎖:?
GETSET lock.foo <current Unix time + lock timeout + 1>?
通過GETSET,C3拿到的時間戳如果仍然是超時的,那就說明,C3如愿以償拿到鎖了。?
如果在C3之前,有個叫C4的客戶端比C3快一步執行了上面的操作,那么C3拿到的時間戳是個未超時的值,這時,C3沒有如期獲得鎖,需要再次等待或重試。留意一下,盡管C3沒拿到鎖,但它改寫了C4設置的鎖的超時值,不過這一點非常微小的誤差帶來的影響可以忽略不計。?

注意:為了讓分布式鎖的算法更穩鍵些,持有鎖的客戶端在解鎖之前應該再檢查一次自己的鎖是否已經超時,再去做DEL操作,因為可能客戶端因為某個耗時的操作而掛起,操作完的時候鎖因為超時已經被別人獲得,這時就不必解鎖了。?

示例偽代碼?
根據上面的代碼,我寫了一小段Fake代碼來描述使用分布式鎖的全過程:?

# get lock 
lock = 0 
while lock != 1: timestamp = current Unix time + lock timeout + 1 lock = SETNX lock.foo timestamp if lock == 1 or (now() > (GET lock.foo) and now() > (GETSET lock.foo timestamp)): break; else: sleep(10ms) # do your job 
do_job() # release 
if now() < GET lock.foo: DEL lock.foo 


是的,要想這段邏輯可以重用,使用python的你馬上就想到了Decorator,而用Java的你是不是也想到了那誰?AOP + annotation?行,怎樣舒服怎樣用吧,別重復代碼就行。?

java之jedis實現?
expireMsecs 鎖持有超時,防止線程在入鎖以后,無限的執行下去,讓鎖無法釋放?
timeoutMsecs 鎖等待超時,防止線程饑餓,永遠沒有入鎖執行代碼的機會?? ?

/*** Acquire lock.* * @param jedis* @return true if lock is acquired, false acquire timeouted* @throws InterruptedException*             in case of thread interruption*/public synchronized boolean acquire(Jedis jedis) throws InterruptedException {int timeout = timeoutMsecs;while (timeout >= 0) {long expires = System.currentTimeMillis() + expireMsecs + 1;String expiresStr = String.valueOf(expires); //鎖到期時間if (jedis.setnx(lockKey, expiresStr) == 1) {// lock acquiredlocked = true;return true;}String currentValueStr = jedis.get(lockKey); //redis里的時間if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {//判斷是否為空,不為空的情況下,如果被其他線程設置了值,則第二個條件判斷是過不去的// lock is expired
String oldValueStr = jedis.getSet(lockKey, expiresStr);//獲取上一個鎖到期時間,并設置現在的鎖到期時間,//只有一個線程才能獲取上一個線上的設置時間,因為jedis.getSet是同步的if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {//如過這個時候,多個線程恰好都到了這里,但是只有一個線程的設置值和當前值相同,他才有權利獲取鎖// lock acquiredlocked = true;return true;}}timeout -= 100;Thread.sleep(100);}return false;}

?

一般用法?
其中很多繁瑣的邊緣代碼?
包括:異常處理,釋放資源等等?? ? ? ?

JedisPool pool;JedisLock jedisLock = new JedisLock(pool.getResource(), lockKey, timeoutMsecs, expireMsecs);try {if (jedisLock.acquire()) { // 啟用鎖//執行業務邏輯} else {logger.info("The time wait for lock more than [{}] ms ", timeoutMsecs);}} catch (Throwable t) {// 分布式鎖異常
            logger.warn(t.getMessage(), t);} finally {if (jedisLock != null) {try {jedisLock.release();// 則解鎖} catch (Exception e) {}}if (jedis != null) {try {pool.returnResource(jedis);// 還到連接池里} catch (Exception e) {}}}

?

?

犀利用法?
用匿名類來實現,代碼非常簡潔?
至于SimpleLock的實現

???????
 SimpleLock lock = new SimpleLock(key);lock.wrap(new Runnable() {@Overridepublic void run() {//此處代碼是鎖上的
            }});

?

轉載于:https://www.cnblogs.com/SimplifyIT/p/6691584.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/371118.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/371118.shtml
英文地址,請注明出處:http://en.pswp.cn/news/371118.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

sql java驅動程序_Microsoft SQL Server JDBC 驅動程序支持矩陣

本頁包含 Microsoft SQL Server JDBC 驅動程序的支持矩陣和支持生命周期策略。Microsoft JDBC 驅動程序支持生命周期矩陣和策略Microsoft 支持生命周期 (MSL) 策略提供了與 Microsoft 產品的支持生命周期有關的可預測透明信息。 自驅動程序發布之日起&#xff0c;JDBC 驅動程序…

使用直接內存時可以更快

總覽 使用直接內存不能保證提高性能。 考慮到它增加了復雜性&#xff0c;除非有充分的理由使用它&#xff0c;否則應避免使用它。 塞爾吉奧奧利維拉&#xff08;Sergio Oliveira Jr&#xff09;的這篇出色文章表明&#xff0c;這不僅僅是使用直接內存來提高性能的問題&#x…

POJ 3977 折半枚舉

鏈接&#xff1a; http://poj.org/problem?id3977 題意&#xff1a; 給你n個數&#xff0c;n最大35&#xff0c;讓你從中選幾個數&#xff0c;不能選0個&#xff0c;使它們和的絕對值最小 如果有一樣的&#xff0c;取個數最小的 題解&#xff1a; np難題&#xff0c;但是因為…

java踩坑記

1.String 相等 稍微有點經驗的程序員都會用equals比較而不是用 &#xff0c;但用equals就真的安全了嗎&#xff0c;看下面的代碼 user.getName().equals("xiaoming"); 有經驗的老司機很快就能看到問題&#xff0c;如果user.getName()為null,就會拋出空指針異常&#…

java taken_java-是否有正確的方法在slf4j中傳遞參數?

第三變種是最好的。實際上&#xff0c;第一種情況是通過StringBuilder進行的字符串連接。第二和第三種情況相同。他們需要將整數值裝箱到Integer(或其他Object)&#xff0c;然后創建一個數組來打包它們。在我的機器上進行的簡單測試表明&#xff0c;如果不執行日志記錄&#xf…

html常用小知識

請求重定向&#xff1a;加載頁面之后&#xff0c;除了用js做重定向之外&#xff0c;我們還可以直接用<meta>標簽做重定向。 1 <meta http-equiv"refresh" content"5;urlhttp://www.baidu.com" /> 5秒后跳轉 超鏈接&#xff1a;在當前的iframe…

MyEclipse快捷鍵大全【轉】

-------------------------------------MyEclipse 快捷鍵1(CTRL)-------------------------------------Ctrl1 快速修復CtrlD: 刪除當前行 CtrlQ 定位到最后編輯的地方 CtrlL 定位在某行 CtrlO 快速顯示 OutLine CtrlT 快速顯示當前類的繼承結構 CtrlW 關閉當前Editer Ct…

根據您的命令-命令設計模式

命令設計模式是一種廣為人知的設計模式&#xff0c;它屬于行為設計模式&#xff08;“四人幫”的一部分&#xff09;。 顧名思義&#xff0c;它與應用程序中的動作和事件有關。 問題陳述&#xff1a; 假設有一個網頁將在其中包含多個菜單的情況。 編寫此代碼的一種方法是使條件…

用js和jQuery做輪播圖

Javascript或jQuery做輪播圖 css樣式 <style> a{ text-decoration:none; } .naver{ width: 100%; position:relative; }.images{position:relative;width: 100%;height: 400px; } .images img{position:absolute;left: 0;top: 0;width: 100%;height: 400px;opacity: 0;fi…

w3school前端教程合集

有關前端開發的w3c教程合集。 http://caibaojian.com/w3school/ 地圖ajax教程Canvas教程CSS教程CSS3教程CSS3選擇器CSS參考手冊DHTML教程HTML教程HTML5教程HTML5音頻教程HTML DOM教程JavaScript教程jQuery教程jQuery Ajax教程jQuery事件jQuery操作jQuery選擇器jQuery遍歷json教…

【開發調試】谷歌瀏覽器中調試移動網頁和測試網速下頁面效果

、 今天有幸給大家分享一下谷歌瀏覽器針對移動網頁測試的技巧&#xff0c;主要是最近做個微信公共號網站。所以就要對頁面測試拉。移動網頁我們最長測得就是各種手機大小的頁面效果和出現網絡問題的效果展示。 今天就簡單分享下在谷歌瀏覽器測試頁面的適配和網速限制展示。…

拼多多分享好友砍價Java實現_拼多多砍價怎么分享到朋友圈 砍價發到微信朋友圈方法...

拼多多是一款電商社交的共享式購物平臺&#xff0c;現在還推出了砍價的活動&#xff0c;只要邀請好友砍價&#xff0c;你就以最低的價格購買商品&#xff0c;還可以可能是免費拿到&#xff0c;那么今天小編就給大家講講如何將自己的砍價信息分享到微信朋友圈。首先下載手機拼多…

通過6個簡單的步驟在Windows上運行Apache Hive

注意 &#xff1a;您需要安裝cygwin才能運行本教程&#xff0c;因為Hadoop&#xff08;Hive需要&#xff09;需要cygwin才能在Windows上運行。 至少&#xff0c;系統中必須存在Basic&#xff0c;Net&#xff08;OpenSSH&#xff0c;tcp_wrapper軟件包&#xff09;和與安全相關的…

vim編輯器初級(八)

:abbreviate  后面接一個縮寫&#xff0c;再接這個縮寫的全寫&#xff0c;這樣在輸入這個縮寫后&#xff0c;vim會自動將其展開為它的全寫 :abbreviate  列出目前你所設置的所有縮寫 :map  后面接一個字符串&#xff0c;再接這個字符串所映射的一串命令&#xff0c;這樣在…

java多文件post請求_如何使用Java發出多部分/表單數據POST請求?

我們使用HttpClient 4.x創建多部分文件post。更新&#xff1a;截至HttpClient 4.3&#xff0c;一些類已被棄用。下面是新API的代碼&#xff1a;CloseableHttpClient httpClient HttpClients.createDefault();HttpPost uploadFile new HttpPost("...");MultipartEnt…

vue 環境的搭建及初始化項目

其實超級簡單&#xff0c;雖然網上很多&#xff0c;但是我順便記錄下相當于做筆記吧 1nodejs 的安裝&#xff0c; 在node官網下載&#xff0c;點擊安裝&#xff0c;安裝的時候最好選擇路徑在d盤 2設置環境變量 我的電腦-->屬性-->系統環境變量- 系統變量新增一個NODE…

Java堆轉儲:您可以完成任務嗎?

如果您像我一樣對Java性能充滿熱情&#xff0c;那么堆轉儲分析對您來說應該不是一個謎。 如果是這樣&#xff0c;那么好消息是您將有機會提高您的Java故障診斷技能和JVM知識。 JVM現已發展到今天&#xff0c;與舊的JDK 1.0 – JDK 1.4天相比&#xff0c;今天生成和分析JVM堆轉…

MariaDB配置、集群

MariaDB在centos 7.3的安裝&#xff0c;配置和集群搭配 阿里云最新選配系統中&#xff0c;只有centos7.3可選&#xff0c;因此&#xff0c;基于centos 7的MariaDB的安裝&#xff0c;配置。。。 全部刪除MySQL/MariaDB MySQL 已經不再包含在 CentOS 7 的源中&#xff0c;而改用了…

java 調用 ictclas50_1-Ictclas50分詞系統ForJava

Ictclas50是一個分詞庫&#xff0c;我嘛主要用來做中文分詞&#xff0c;其也能分出詞性等東西。1.環境搭建進入到下載頁面進行下載&#xff1a;如下圖&#xff1a; 因為我的系統是64位的windows&#xff0c;所以選擇了到數第三行進行下載。其JAVA版本是通過JNI去調用dll庫&…

SpringMVC亂碼或前臺亂碼解決辦法

JSP頁面亂碼 <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> 以及 form表單提交方式為必須為post 修改web.xml&#xff0c;增加編碼過濾器&#xff0c;如下&#xff08;注意&#xff0c;需要設置forceEncoding參數值…