java結合redis實現分散式鎖
今天工作之餘,檢視一下利用redis來實現分散式鎖,因此,在檢視別人文章之餘,自己也來手動模擬實現Java的lock介面,來自己手動實現一個分散式鎖。擁有簡單的加鎖,解鎖,鎖中斷等操作。
利用redis的分散式鎖,主要還是利用redis的setnx命令,檢視redis文件,可知次命令在redis快取中新增資料的時候,如果key存在,則新增資料操作不成功。若不存在,才可以新增成功。從另外一個方面來理解鎖(Lock),其實就是一種資源,在某個時刻標記為只能被某個執行緒使用,若資源已經被使用,則其他執行緒必須等待當前執行緒釋放資源之後才可以使用。從這個方面理解,也就是,當前執行緒持有在redis快取中key的資源,所以其他執行緒必須等待當前執行緒釋放key的資源,否則只能等待。下面來貼上程式碼具體分析下:
package com.redislock;
import redis.clients.jedis.Jedis;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* 嘗試使用redis實現分散式鎖
* Created by hadoop on 2017/3/16.
*/
public class RedisLock implements Lock{
/**jedis客戶端**/ private final Jedis jedis;
在這裡,第一,我是利用實現Lock介面的方式來實現這個鎖,在使用鎖的時候,一般為一個執行緒持有一把鎖,因此鎖可以看成是線上程自己的內部堆疊裡面,因此可以不用考慮多執行緒的支援。重點是在獲取鎖的介面,獲取鎖意味著對資源的競爭,因此在本示例中,我嘗試迴圈獲取物件的鎖,也就是迴圈向redis裡面新增資料,新增成功,則代表加鎖成功,新增不成功,則代表加鎖失敗,休眠一段時間之後,繼續獲取鎖。因此這個lock方法為阻塞的,獲取不到鎖,就會一直去獲取。newCondition方法沒有實現,也是基於目前才疏學淺,還不回使用,後面會考慮加上這個newCondition方法。/**鎖定資源的key**/ private final String lockName; /**持有鎖的最長時間**/ private final int expireTime = 300; /**獲取不到鎖的休眠時間**/ private final long sleepTime = 100; /**鎖中斷狀態**/ private boolean interruped = true; /**超時時間**/ private long expireTimeOut = 0; public RedisLock(Jedis jedis, String lockName){ this.jedis = jedis; this.lockName = lockName; } public void lock() { if (jedis == null) throw new NullPointerException("jedis is null"); if (lockName == null) throw new NullPointerException("key is null"); while (true){ if (!interruped) throw new RuntimeException("獲取鎖狀態被中斷"); long id = jedis.setnx(lockName, lockName); if (id == 0L){ try { Thread.currentThread().sleep(this.sleepTime); }catch (InterruptedException e){ e.printStackTrace(); } }else{ expireTimeOut = System.currentTimeMillis()/1000 + expireTime; jedis.expireAt(this.lockName, expireTimeOut); break; } } } public void lockInterruptibly() throws InterruptedException { this.interruped = false; } public boolean tryLock() { if (jedis == null) throw new NullPointerException("jedis is null"); if (lockName == null) throw new NullPointerException("lockName is null"); if (!interruped) throw new RuntimeException("執行緒被中斷"); long id = jedis.setnx(lockName, lockName); if (id == 0L) return false; else { // 設定鎖過期時間 expireTimeOut = System.currentTimeMillis()/1000 + expireTime; jedis.expireAt(this.lockName, expireTimeOut); return true; } } public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { if (jedis == null) throw new NullPointerException("jedis is null"); if (lockName == null) throw new NullPointerException("lockName is null"); if (time == 0) return false; long now = System.currentTimeMillis(); long timeOutAt = now + calcSeconds(time, unit); while (true){ if (!interruped) throw new InterruptedException("執行緒被中斷"); long id = jedis.setnx(this.lockName, this.lockName); // id = 0 表示加鎖失敗 if(id == 0){ // 獲取鎖超時 if(System.currentTimeMillis() > timeOutAt) return false; try { // 休眠一段時間,繼續獲取鎖 Thread.currentThread().sleep(this.sleepTime); }catch (InterruptedException e){ e.printStackTrace(); } }else { //獲取鎖成功,設定鎖過期時間 expireTimeOut = System.currentTimeMillis()/1000 + expireTime; jedis.expireAt(this.lockName, expireTimeOut); return true; } } } public void unlock() { try { //當前時間小於過期時間,則鎖未超時,刪除鎖定 if (System.currentTimeMillis() / 1000 < expireTimeOut) jedis.del(lockName); }catch (Exception e){ }finally { jedis.close(); } } public Condition newCondition() { throw new UnsupportedOperationException("不支援當前的操作"); } /** * 時間轉換成毫秒 * @param time * @param unit * @return */ private long calcSeconds (long time, TimeUnit unit){ if (unit == TimeUnit.DAYS) return time * 24 * 60 * 60 * 1000; else if (unit == TimeUnit.HOURS) return time * 60 * 60 * 1000; else if (unit == TimeUnit.MINUTES) return time * 60 * 1000; else return time * 1000; } }
在這裡,說兩點,第一點就是redis key的超時,這樣可以保證在客戶端程式突然down的時候,資源可以在一段時間之後被釋放掉,不會產生死鎖。還有就是unlock()這個方法,在加鎖的時候,會計算鎖釋放的時間,在釋放鎖的時候,要判定當前鎖是否超時了,若超時,怎不能刪除key。否則會破壞執行緒的安全性。因為不用的redis客戶端,雖然不能setnx key值,但是可以del這個key。當然,我這裡是一種最簡單的處理,在臨界條件下也容易出現問題,只是目前可以想到的一種辦法。也希望大神們不吝賜教小弟我。
第二點需要說明的是中斷狀態,基於以前看多執行緒安全結束的模式,在此處我也是設定標誌量interruped 用來標識當前的鎖已經被中斷,在進行任何操作前,都會先判定這個中斷標誌,若被中斷,則丟擲程式異常。 目前還不知道這樣做到底會如何,希望路過大神給指導一下意見!!!完全自學,希望不吝賜教。在寫的過程中,考慮到一件事情,若中斷標誌被改變,也就是鎖被中斷,是否需要呼叫unlock()方法釋放這個鎖???
留下諸多疑問,希望路過的朋友留下自己的意見!
相關推薦
java結合redis實現分散式鎖
今天工作之餘,檢視一下利用redis來實現分散式鎖,因此,在檢視別人文章之餘,自己也來手動模擬實現Java的lock介面,來自己手動實現一個分散式鎖。擁有簡單的加鎖,解鎖,鎖中斷等操作。 利用redis的分散式鎖,主要還是利用redis的setnx命令,檢視redis文件,
Java使用Redis實現分散式鎖
package com.jikefriend.test.common.jedis; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.RandomUtils; import redis.client
【redis】使用redisTemplate優雅地操作redis及使用redis實現分散式鎖
前言: 上篇已經介紹了redis及如何安裝和叢集redis,這篇介紹如何通過工具優雅地操作redis. Long Long ago,程式猿們還在通過jedis來操作著redis,那時候的猿類,一個個累的沒日沒夜,重複的造著輪子,忙得沒時間陪家人,終於有一天猿類的春天來了,spring家族的r
基於Redis實現分散式鎖
背景 在很多網際網路產品應用中,有些場景需要加鎖處理,比如:秒殺,全域性遞增ID,樓層生成等等。大部分的解決方案是基於DB實現的,Redis為單程序單執行緒模式,採用佇列模式將併發訪問變成序列訪問,且多客戶端對Redis的連線並不存在競爭關係。其次Redis提供一些命令SETNX,GETSET,可以方便
利用Redis實現分散式鎖 使用mysql樂觀鎖解決併發問題
寫在最前面 我在之前總結冪等性的時候,寫過一種分散式鎖的實現,可惜當時沒有真正應用過,著實的心虛啊。正好這段時間對這部分實踐了一下,也算是對之前填坑了。 分散式鎖按照網上的結論,大致分為三種:1、資料庫樂觀鎖; 2、基於Redis的分散式鎖;3.、基於ZooKeeper的分散式鎖; 關於樂觀鎖的實現其實
利用Redis實現分散式鎖
為什麼需要分散式鎖? 在傳統單體應用單機部署的情況下,可以使用Java併發相關的鎖,如ReentrantLcok或synchronized進行互斥控制。但是,隨著業務發展的需要,原單體單機部署的系統,漸漸的被部署在多機器多JVM上同時提供服務,這使得原單機部署情況下的併發控制鎖策略失效了,為了解決這個問
如何用 Redis 實現分散式鎖和超時情況處理
目前各種分散式的架構和微服務架構無處不在,在這種類似架構中處理併發和分散式併發問題,本場 Chat 就主要以 Redis 為例,使用分散式鎖的方式如何處理併發問題和避免超時情況的出現,主要從以下幾個方面講述: Redis 的 Setnx 命令是如何實現分散式鎖的; Setnx 的實現鎖的
ZooKeeper分散式鎖簡單實踐 利用Redis實現分散式鎖
寫在最前面 前幾周寫了篇 利用Redis實現分散式鎖 ,今天簡單總結下ZooKeeper實現分散式鎖的過程。其實生產上我只用過Redis或者資料庫的方式,之前還真沒了解過ZooKeeper怎麼實現分散式鎖。這周簡單寫了個小Demo,更堅定了我繼續使用Redis的信心了。 ZooKeep
分散式鎖-使用Redis實現分散式鎖
使用Redis實現分散式鎖 關於分散式鎖的實現,我的前一篇文章講解了如何使用Zookeeper實現分散式鎖。關於分散式鎖的背景此處不再做贅述,我們直接討論下如何使用Redis實現分散式鎖。 關於Redis,筆主不打算做長篇大論的介紹,只介紹下Redis優秀的特性
REDIS 學習(10)流程圖解使用redis實現分散式鎖
redis作為集中式快取,可以通過它來實現分散式鎖。 首先用到的redis操作有: setnx key value: 當key不存在的時候生效並返回1,當已經有此key的時候返回0 getset key value:
RedLock演算法-使用redis實現分散式鎖服務
譯自Redis官方文件 在多執行緒共享臨界資源的場景下,分散式鎖是一種非常重要的元件。 許多庫使用不同的方式使用redis實現一個分散式鎖管理。 其中有一部分簡單的實現方式可靠性不足,可以通過一些簡單的修改提高其可靠性。 這篇文章介紹了一種指導性的redis分散式鎖演算法RedLock,RedL
ASP.NET結合Redis實現分散式快取
最近一個專案ASP.NET+MySQL 有的網頁開啟初始化的查詢需要10秒甚至更久,使用者體驗極差,而且併發量變大的時候網站容易崩潰 後來想了兩種解決方案都不是太滿意 1、資料庫裡建一張快取表,後臺作業定時去更新這張表,每次網頁開啟就可以直接從快取表裡查詢 2、使用者第一次開啟網站將資
Redis實現分散式鎖(spring定時任務叢集應用Redis分散式鎖)
之前2片文章介紹了 描述: 不管用不用動態執行,單機服務都是沒有問題的,但是如果服務是叢集模式下,那麼一個任務在每臺機器都會執行一次,這肯定不是我們需要的,我們要實現的是整個叢集每次只有一個任務執行成功,但是spring
【Redis實現分散式鎖】Redis實現分散式鎖
前言 分散式鎖一般有三種實現方式:1. 資料庫樂觀鎖;2. 基於Redis的分散式鎖;3. 基於ZooKeeper的分散式鎖。本篇部落格將介紹第二種方式,基於Redis實現分散式鎖。雖然網上已經有各種介紹Redis分散式鎖實現的部落格,然而他們的實現卻有著各種各樣的問題,為
使用redis實現分散式鎖
大致思路: 加鎖: 利用set方法的nx,px引數: nx引數表示當key不存在時才成功,key存在時失敗,故保證了排他性. px引數表示設定過期時間,保證了執行緒出現異常無法釋放鎖(刪除key) 釋放鎖: 不能簡單地刪除鍵,因為可能出現這樣的情況:執行緒成功se
Redis實現分散式鎖機制
Redis實現分散式鎖思路 常用的是redis函式是setnx(),這個應該是實現分散式鎖最主要的函式。首先是將某一業務標識名作為鍵存到redis裡,併為其設個過期時間,如果是還有加鎖請求過來,先是通過setnx()看看是否能將鎖的標識插入到redis裡,可
基於 Redis 實現分散式鎖
什麼是Redis? Redis通常被稱為資料結構伺服器。這意味著Redis通過一組命令提供對可變資料結構的訪問,這些命令使用帶有TCP套接字和簡單協議的伺服器 - 客戶端模型傳送。因此,不同的程序可以以共享方式查詢和修改相同的資料結構。 Redis中實現的資料結構有一些特殊屬性:
redis實現分散式鎖(基於lua指令碼操作)
lua指令碼能保證redis的原子性操作,redis使用springboot的redistemplate /** * create by abel * create date 2018/11/16 11:28 * describe:請輸入專案描述 */ public class
什麼是分散式鎖?Redis實現分散式鎖詳解
在很多場景中,我們為了保證資料的最終一致性,需要很多的技術方案來支援,比如分散式事務、分散式鎖等。那具體什麼是分散式鎖,分散式鎖應用在哪些業務場景、如何來實現分散式鎖呢?今天繼續由陳睿|mikechen來繼續分享Redis這個系列。 01.什麼是分散式鎖
redis實現分散式鎖
/** * 分散式鎖的簡單實現程式碼 */ public class DistributedLock { private final JedisPool jedisPool; public DistributedLock(JedisPool