1. 程式人生 > >java結合redis實現分散式鎖

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;
    /**鎖定資源的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;
    }
}
在這裡,第一,我是利用實現Lock介面的方式來實現這個鎖,在使用鎖的時候,一般為一個執行緒持有一把鎖,因此鎖可以看成是線上程自己的內部堆疊裡面,因此可以不用考慮多執行緒的支援。重點是在獲取鎖的介面,獲取鎖意味著對資源的競爭,因此在本示例中,我嘗試迴圈獲取物件的鎖,也就是迴圈向redis裡面新增資料,新增成功,則代表加鎖成功,新增不成功,則代表加鎖失敗,休眠一段時間之後,繼續獲取鎖。因此這個lock方法為阻塞的,獲取不到鎖,就會一直去獲取。newCondition方法沒有實現,也是基於目前才疏學淺,還不回使用,後面會考慮加上這個newCondition方法。

在這裡,說兩點,第一點就是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