欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis如何實現(xiàn)分布式鎖詳解

 更新時間:2021年06月01日 10:55:14   作者:kusedexingfu  
分布式鎖一般有三種實現(xiàn)方式:1. 數(shù)據(jù)庫樂觀鎖;2. 基于Redis的分布式鎖;3. 基于ZooKeeper的分布式鎖.本篇文章將介紹第二種方式,基于Redis實現(xiàn)分布式鎖,文中有非常詳細的介紹,需要的朋友可以參考下

一、前言

在Java的并發(fā)編程中,我們通過鎖,來避免由于競爭而造成的數(shù)據(jù)不一致問題。通常,我們以synchronized 、Lock來使用它。

但是Java中的鎖,只能保證在同一個JVM進程內(nèi)中執(zhí)行。如果在分布式集群環(huán)境下,就需要分布式鎖了。

通常的分布式鎖的實現(xiàn)方式有redis,zookeeper,但是一般我們的程序中都會用到redis,用redis做分布式鎖,也能夠降低成本。

二、實現(xiàn)原理

2.1 加鎖

加鎖實際上就是在redis中,給Key鍵設(shè)置一個值,為避免死鎖,并給定一個過期時間。

在Redis 2.6.12以及之前,可以通過setnx key value (key不存在才設(shè)置成功)設(shè)置值,通過expire key seconds設(shè)置過期時間。但是由于是兩個命令,不是原子的。如果在設(shè)置值之后還沒有來得及設(shè)置過期時間,程序掛掉了,那么這個key就永遠的存在redis中了。

在Redis 2.6.12之后,redis提供了set key value EX seconds NX 和set key value PX millisecond NX  來原子性的設(shè)置值和設(shè)置過期時間。

2.2 解鎖

解鎖的過程就是將Key鍵刪除。但也不能亂刪,不能說客戶端1的請求將客戶端2的鎖給刪除掉,在刪除前需要判斷是不是設(shè)置的value,如果是才刪除。

為了保證解鎖操作的原子性,我們用LUA腳本完成這一操作。先判斷當(dāng)前鎖的字符串是否與傳入的值相等,是的話就刪除Key,解鎖成功。LUA腳本如下:

if redis.call('get',KEYS[1]) == ARGV[1] then
   return redis.call('del',KEYS[1])
else
   return 0
end

三、通過RedisTemplate實現(xiàn)分布式鎖

我們通過SpringBoot構(gòu)建的應(yīng)用程序一般都是用RedisTemplate來操作緩存redis。下面介紹基于RedisTemplate來實現(xiàn)分布式鎖。

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
 
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
 
@Service
@Slf4j
public class RedisLockUtil {
 
    @Autowired
    private RedisTemplate redisTemplate;
 
    //獲取鎖超時時間
    private long timeout = 60000;
 
    /**
     * 獲取鎖
     * @param key
     * @param value
     * @param ms
     * @return
     */
    public Boolean getLock(String key, String value, Long ms) {
        long startTime = System.currentTimeMillis();
        while (true) {
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, value, ms, TimeUnit.MILLISECONDS);
            if (flag) {
                return true;
            }
 
            //避免一直無限獲取鎖
            if (System.currentTimeMillis() - startTime > timeout) {
                return false;
            }
 
            try {
                log.info("{}重試鎖", key);
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
 
    /**
     * 解鎖
     * @param key
     * @param value
     * @return
     */
    public Boolean unLock(String key, String value) {
        String script =
                "if redis.call('get',KEYS[1]) == ARGV[1] then" +
                        "   return redis.call('del',KEYS[1]) " +
                        "else" +
                        "   return 0 " +
                        "end";
 
        // 構(gòu)造RedisScript并指定返回類類型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        // 參數(shù)一:redisScript,參數(shù)二:key列表,參數(shù)三:arg(可多個)
        Object result = redisTemplate.execute(redisScript, Arrays.asList(key), value);
 
        return "1".equals(result.toString());
    }
}

四、通過Redisson實現(xiàn)

Redisson為我們封裝了細節(jié),可以開箱即用。

引入maven依賴:

<dependency>
	<groupId>org.redisson</groupId>
	<artifactId>redisson</artifactId>
	<version>3.14.0</version>
</dependency>

配置類:

import lombok.Data;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
/**
 * redisson 配置類
 */
@Configuration
@Data
public class RedissonConfig {
 
    @Value("${spring.redis.host}")
    private String host;
 
    @Value("${spring.redis.port}")
    private String port;
 
    @Value("${spring.redis.password}")
    private String password;
 
    @Bean
    public RedissonClient getRedisson(){
 
        Config config = new Config();
        config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password);
        //添加主從配置
//        config.useMasterSlaveServers().setMasterAddress("").setPassword("").addSlaveAddress(new String[]{"",""});
 
        return Redisson.create(config);
    }
}

redis屬性配置:

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: root

封裝的加鎖解鎖工具類:

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
import java.util.concurrent.TimeUnit;
 
/**
 * redis分布式鎖幫助類
 */
@Component
public class RedissLockUtil {
 
    @Autowired
    private RedissonClient redissonClient;
    
 
    /**
     * 加鎖
     * @param lockKey
     * @return
     */
    public RLock lock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        return lock;
    }
 
    /**
     * 釋放鎖
     * @param lockKey
     */
    public  void unlock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.unlock();
    }
    
    /**
     * 釋放鎖
     * @param lock
     */
    public void unlock(RLock lock) {
        lock.unlock();
    }
 
    /**
     * 帶超時的鎖
     * @param lockKey
     * @param timeout 超時時間   單位:秒
     */
    public RLock lock(String lockKey, int timeout) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, TimeUnit.SECONDS);
        return lock;
    }
    
    /**
     * 帶超時的鎖
     * @param lockKey
     * @param unit 時間單位
     * @param timeout 超時時間
     */
    public RLock lock(String lockKey, TimeUnit unit , int timeout) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, unit);
        return lock;
    }
    
    /**
     * 嘗試獲取鎖
     * @param lockKey
     * @param waitTime 最多等待時間
     * @param leaseTime 上鎖后自動釋放鎖時間
     * @return
     */
    public boolean tryLock(String lockKey, int waitTime, int leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            return false;
        }
    }
    
    /**
     * 嘗試獲取鎖
     * @param lockKey
     * @param unit 時間單位
     * @param waitTime 最多等待時間
     * @param leaseTime 上鎖后自動釋放鎖時間
     * @return
     */
    public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            return false;
        }
    }
}

到此這篇關(guān)于Redis如何實現(xiàn)分布式鎖詳解的文章就介紹到這了,更多相關(guān)Redis分布式鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java集合之Set接口及其實現(xiàn)類精解

    Java集合之Set接口及其實現(xiàn)類精解

    set接口是繼承自Collection的子接口,特點是元素不重復(fù),存儲無序。在set接口的實現(xiàn)類中添加重復(fù)元素是不會成功的,判斷兩個元素是否重復(fù)根據(jù)元素類重寫的
    2021-09-09
  • Spring Boot項目中集成微信支付v3

    Spring Boot項目中集成微信支付v3

    這篇文章主要介紹了Spring Boot項目中集成微信支付v3,幫助大家更好的理解和使用spring boot框架,感興趣的朋友可以了解下
    2021-01-01
  • spring源碼下載、編譯、debug的詳細教程

    spring源碼下載、編譯、debug的詳細教程

    這篇文章主要介紹了spring源碼下載、編譯、debug的詳細教程,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10
  • Spring Boot 整合 Mockito提升Java單元測試的高效實踐案例

    Spring Boot 整合 Mockito提升Java單元測試的高效實踐案例

    Mockito與Spring Boot的整合為Java開發(fā)者提供了一套完整的解決方案,使得單元測試更為精準(zhǔn)、高效,從而確保了代碼質(zhì)量、降低了維護成本,并促進了項目的持續(xù)集成與交付,感興趣的朋友跟隨小編一起看看吧
    2024-04-04
  • 基于spring DI的三種注入方式分析

    基于spring DI的三種注入方式分析

    這篇文章主要介紹了基于spring DI的三種注入方式分析,具有很好的參考價值,希望對大家有所幫助。
    2021-07-07
  • Spring boot集成swagger2生成接口文檔的全過程

    Spring boot集成swagger2生成接口文檔的全過程

    這篇文章主要給大家介紹了關(guān)于Spring boot集成swagger2生成接口文檔的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用Spring boot具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • JGroups實現(xiàn)聊天小程序

    JGroups實現(xiàn)聊天小程序

    這篇文章主要為大家詳細介紹了JGroups實現(xiàn)聊天小程序,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • java substring 截取字符串的方法

    java substring 截取字符串的方法

    這篇文章主要介紹了java substring 截取字符串的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • Java8中List轉(zhuǎn)Map(Collectors.toMap) 的技巧分享

    Java8中List轉(zhuǎn)Map(Collectors.toMap) 的技巧分享

    在最近的工作開發(fā)之中,慢慢習(xí)慣了很多Java8中的Stream的用法,很方便而且也可以并行的去執(zhí)行這個流,這篇文章主要給大家介紹了關(guān)于Java8中List轉(zhuǎn)Map(Collectors.toMap) 的相關(guān)資料,需要的朋友可以參考下
    2021-07-07
  • 基于Java SWFTools實現(xiàn)把pdf轉(zhuǎn)成swf

    基于Java SWFTools實現(xiàn)把pdf轉(zhuǎn)成swf

    這篇文章主要介紹了基于Java SWFTools實現(xiàn)把pdf轉(zhuǎn)成swf,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-11-11

最新評論