最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot使用Redis實(shí)現(xiàn)分布式鎖

 更新時(shí)間:2020年05月16日 15:28:01   作者:WP七夕  
這篇文章主要為大家詳細(xì)介紹了SpringBoot使用Redis實(shí)現(xiàn)分布式鎖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

前言

在單機(jī)應(yīng)用時(shí)代,我們對(duì)一個(gè)共享的對(duì)象進(jìn)行多線程訪問(wèn)的時(shí)候,使用java的synchronized關(guān)鍵字或者ReentrantLock類對(duì)操作的對(duì)象加鎖就可以解決對(duì)象的線程安全問(wèn)題。

分布式應(yīng)用時(shí)代這個(gè)方法卻行不通了,我們的應(yīng)用可能被部署到多臺(tái)機(jī)器上,運(yùn)行在不同的JVM里,一個(gè)對(duì)象可能同時(shí)存在多臺(tái)機(jī)器的內(nèi)存中,怎樣使共享對(duì)象同時(shí)只被一個(gè)線程處理就成了一個(gè)問(wèn)題。

在分布式系統(tǒng)中為了保證一個(gè)對(duì)象在高并發(fā)的情況下只能被一個(gè)線程使用,我們需要一種跨JVM的互斥機(jī)制來(lái)控制共享資源的訪問(wèn),此時(shí)就需要用到我們的分布式鎖了。

分布式鎖一般有三種實(shí)現(xiàn)方式:1.通過(guò)數(shù)據(jù)庫(kù)實(shí)現(xiàn)分布式鎖;2.通過(guò)緩存(Redis等)實(shí)現(xiàn)分布式鎖;3.通過(guò)Zookeeper實(shí)現(xiàn)分布式鎖。本篇文章主要介紹第二種通過(guò)Redis實(shí)現(xiàn)分布式鎖的方式。

分布式鎖的需要具備的條件

為了保證分布式鎖的可用性,需要具備一下五點(diǎn)條件:

1、在同一時(shí)間保證只有一臺(tái)機(jī)器的一個(gè)線程可以持有鎖。
2、不能發(fā)生死鎖,無(wú)論何時(shí)持有鎖的機(jī)器崩潰掛掉了都要能自動(dòng)釋放鎖。
3、高效的獲取和釋放鎖。
4、具備非阻塞性,一旦獲取不到鎖就立刻返回加鎖失敗。
5、獨(dú)占性,即自己加的鎖只有自己才能釋放。

代碼實(shí)現(xiàn)

組件依賴

首先在pom.xml文件中添加依賴:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

加鎖代碼

代碼如下:

/**
 * 獲取鎖
 * @param lockKey 鎖
 * @param identity 身份標(biāo)識(shí)(保證鎖不會(huì)被其他人釋放)
 * @param expireTime 鎖的過(guò)期時(shí)間(單位:秒)
 * @return
 */
public boolean lock(String lockKey, String identity, long expireTime){
  boolean lockResult = redisTemplate.opsForValue().setIfAbsent(lockKey, identity, expireTime, TimeUnit.SECONDS);
  return opsForValue;
}

加鎖的方法只需要三個(gè)參數(shù):lockKey、identity、expireTime。

  • 第一個(gè)參數(shù)lockKey為key,一個(gè)資源對(duì)應(yīng)一個(gè)唯一的key。
  • 第二個(gè)參數(shù)identity為身份標(biāo)識(shí),作為此key對(duì)應(yīng)的value存儲(chǔ),為了判斷在釋放鎖時(shí)是不是和加鎖的身份相同,防止別人釋放鎖。
  • 第三個(gè)參數(shù)expireTime為過(guò)期時(shí)間,此參數(shù)保證程序加鎖后崩潰導(dǎo)致不能主動(dòng)釋放鎖的時(shí)候自動(dòng)釋放鎖,防止出現(xiàn)死鎖。

為什么使用setIfAbsent方法呢?這個(gè)方法的好處就是,如果redis中已經(jīng)存在這個(gè)key了,就會(huì)返回失敗,并且不改變r(jià)edis中的數(shù)據(jù),這樣就不會(huì)把別的線程的加的鎖給覆蓋掉。

解鎖代碼

代碼如下:

/**
 * 釋放鎖
 * @param lockKey 鎖
 * @param identity 身份標(biāo)識(shí)(保證鎖不會(huì)被其他人釋放)
 * @return
 */
public boolean releaseLock(String lockKey, String identity){
  String luaScript =
    "if " +
    "  redis.call('get', KEYS[1]) == ARGV[1] " +
    "then " +
    "  return redis.call('del', KEYS[1]) " +
    "else " +
    "  return 0 " +
    "end";
  DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
  redisScript.setResultType(Boolean.class);
  redisScript.setScriptText(luaScript);
  List<String> keys = new ArrayList<>();
  keys.add(lockKey);
  boolean result = redisTemplate.execute(redisScript, keys, identity);
  return result;
}

解鎖的方法只需兩個(gè)參數(shù):lockKey、identity。

  • 第一個(gè)參數(shù)lockKey為key,一個(gè)資源對(duì)應(yīng)一個(gè)唯一的key。
  • 第二個(gè)參數(shù)identity為身份標(biāo)識(shí),作為此key對(duì)應(yīng)的value存儲(chǔ),為了判斷在釋放鎖時(shí)是不是和加鎖的身份相同,防止別人釋放鎖。

此處使用Lua腳本來(lái)判斷身份,身份相同就刪除,身份不同就不對(duì)數(shù)據(jù)做操作并返回失敗。為什么要使用Lua腳本呢?這是為了要保證操作的原子性,redis在執(zhí)行Lua腳本的時(shí)候是把腳本當(dāng)作一個(gè)命令來(lái)執(zhí)行的,我們都知道redis的命令是都是原子操作,這樣就保證了操作的原子性。

測(cè)試代碼

package com.qixi.lock.demo.lockdemo.controller;

import com.qixi.lock.demo.lockdemo.util.RedisLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 測(cè)試分布式鎖
 * @author ZhengNC
 * @date 2020/5/13 17:27
 */
@RestController
@RequestMapping("test")
public class TestRedisLockController {

  private final String lockKeyName = "testKey";

  @Autowired
  private RedisLock redisLock;

  /**
   * 測(cè)試加鎖
   * @param id 加鎖的資源id
   * @param identity 身份標(biāo)識(shí)
   * @return
   */
  @GetMapping("lock")
  public String lock(@RequestParam("id") String id,
            @RequestParam("identity") String identity){
    String lockKey = lockKeyName+":"+id;
    boolean lockSuccess = redisLock.lock(lockKey, identity, 60);
    String result = "lock failed";
    if (lockSuccess){
      result = "lock success";
    }
    return result;
  }

  /**
   * 測(cè)試釋放鎖
   * @param id 釋放鎖的資源id
   * @param identity 身份標(biāo)識(shí)
   * @return
   */
  @GetMapping("release")
  public String release(@RequestParam("id") String id,
            @RequestParam("identity") String identity){
    String lockKey = lockKeyName+":"+id;
    boolean releaseSuccess = redisLock.releaseLock(lockKey, identity);
    String result = "release failed";
    if (releaseSuccess){
      result = "release success";
    }
    return result;
  }
}
package com.qixi.lock.demo.lockdemo.util;

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.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 分布式鎖Redis工具類
 * @author ZhengNC
 * @date 2020/5/13 17:27
 */
@Component
public class RedisLock {

  @Autowired
  private RedisTemplate<String, String> redisTemplate;

  /**
   * 獲取鎖
   * @param lockKey 鎖
   * @param identity 身份標(biāo)識(shí)(保證鎖不會(huì)被其他人釋放)
   * @param expireTime 鎖的過(guò)期時(shí)間(單位:秒)
   * @return
   */
  public boolean lock(String lockKey, String identity, long expireTime){
    boolean lockResult = redisTemplate.opsForValue().setIfAbsent(lockKey, identity, expireTime, TimeUnit.SECONDS);
    return lockResult;
  }

  /**
   * 釋放鎖
   * @param lockKey 鎖
   * @param identity 身份標(biāo)識(shí)(保證鎖不會(huì)被其他人釋放)
   * @return
   */
  public boolean releaseLock(String lockKey, String identity){
    String luaScript =
        "if " +
        "  redis.call('get', KEYS[1]) == ARGV[1] " +
        "then " +
        "  return redis.call('del', KEYS[1]) " +
        "else " +
        "  return 0 " +
        "end";
    DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
    redisScript.setResultType(Boolean.class);
    redisScript.setScriptText(luaScript);
    List<String> keys = new ArrayList<>();
    keys.add(lockKey);
    boolean result = redisTemplate.execute(redisScript, keys, identity);
    return result;
  }
}

結(jié)語(yǔ)

感謝大家閱讀我的文章,更歡迎大家指出我的問(wèn)題,希望能在這里通過(guò)討論取得共同的進(jìn)步。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java并發(fā)之原子性 有序性 可見性及Happen Before原則

    Java并發(fā)之原子性 有序性 可見性及Happen Before原則

    一提到happens-before原則,就讓人有點(diǎn)“丈二和尚摸不著頭腦”。這個(gè)涵蓋了整個(gè)JMM中可見性原則的規(guī)則,究竟如何理解,把我個(gè)人一些理解記錄下來(lái)。下面可以和小編一起學(xué)習(xí)Java 并發(fā)四個(gè)原則
    2021-09-09
  • SpringBoot接口防抖(防重復(fù)提交)的實(shí)現(xiàn)方案

    SpringBoot接口防抖(防重復(fù)提交)的實(shí)現(xiàn)方案

    所謂防抖,一是防用戶手抖,二是防網(wǎng)絡(luò)抖動(dòng),在Web系統(tǒng)中,表單提交是一個(gè)非常常見的功能,如果不加控制,容易因?yàn)橛脩舻恼`操作或網(wǎng)絡(luò)延遲導(dǎo)致同一請(qǐng)求被發(fā)送多次,所以本文給大家介紹了SpringBoot接口防抖(防重復(fù)提交)的實(shí)現(xiàn)方案,需要的朋友可以參考下
    2024-04-04
  • java實(shí)現(xiàn)服務(wù)器文件打包zip并下載的示例(邊打包邊下載)

    java實(shí)現(xiàn)服務(wù)器文件打包zip并下載的示例(邊打包邊下載)

    這篇文章主要介紹了java實(shí)現(xiàn)服務(wù)器文件打包zip并下載的示例,使用該方法,可以即時(shí)打包文件,一邊打包一邊傳輸,不使用任何的緩存,讓用戶零等待,需要的朋友可以參考下
    2014-04-04
  • Springboot @Validated和@Valid的區(qū)別及使用詳解

    Springboot @Validated和@Valid的區(qū)別及使用詳解

    這篇文章主要介紹了Springboot @Validated和@Valid的區(qū)別及使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • 關(guān)于 Java 的數(shù)據(jù)結(jié)構(gòu)鏈表

    關(guān)于 Java 的數(shù)據(jù)結(jié)構(gòu)鏈表

    這篇文章主要介紹了關(guān)于 Java 的數(shù)據(jù)結(jié)構(gòu)鏈表的相關(guān)資料,需要的朋友可以參考下面文章內(nèi)容
    2021-09-09
  • Mybatis核心配置文件加載流程詳解

    Mybatis核心配置文件加載流程詳解

    本文將介紹MyBatis在配置文件加載的過(guò)程中,如何加載核心配置文件、如何解析映射文件中的SQL語(yǔ)句以及每條SQL語(yǔ)句如何與映射接口的方法進(jìn)行關(guān)聯(lián),具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12
  • 關(guān)于Java8中map()和flatMap()的一些事

    關(guān)于Java8中map()和flatMap()的一些事

    這篇文章主要給大家介紹了關(guān)于Java8中map()和flatMap()的一些事,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • Spring Boot 中的任務(wù)執(zhí)行器基本概念及使用方法

    Spring Boot 中的任務(wù)執(zhí)行器基本概念及使用方法

    務(wù)執(zhí)行器是 Spring Boot 中的一個(gè)非常實(shí)用的模塊,它可以簡(jiǎn)化異步任務(wù)的開發(fā)和管理,在本文中,我們介紹了任務(wù)執(zhí)行器的基本概念和使用方法,以及一個(gè)完整的示例代碼,需要的朋友可以參考下
    2023-07-07
  • Java中String的intern()方法詳細(xì)說(shuō)明

    Java中String的intern()方法詳細(xì)說(shuō)明

    這篇文章主要介紹了Java中String的intern()方法詳細(xì)說(shuō)明,String::intern()是一個(gè)本地方法,他的作用就是如果字符串常量池中已經(jīng)包含了一個(gè)等于此String對(duì)象的字符串,則返回代表池中的這個(gè)字符串額String對(duì)象的引用,需要的朋友可以參考下
    2023-11-11
  • Mybatis通用Mapper(tk.mybatis)的使用

    Mybatis通用Mapper(tk.mybatis)的使用

    本文主要介紹了Mybatis通用Mapper(tk.mybatis)的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07

最新評(píng)論

繁峙县| 万全县| 萨嘎县| 香格里拉县| 莫力| 光山县| 西安市| 阿拉尔市| 义马市| 永胜县| 永胜县| 湘乡市| 仪征市| 禄劝| 北辰区| 乌鲁木齐市| 阳原县| 武威市| 白玉县| 北川| 烟台市| 三都| 闵行区| 富顺县| 黄山市| 嘉鱼县| 铅山县| 杭锦后旗| 阜平县| 高雄市| 南皮县| 吕梁市| 普兰县| 黔西县| 卢氏县| 聊城市| 普兰店市| 区。| 遵义县| 湖口县| 天峻县|