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

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

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

前言

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

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

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

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

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

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

1、在同一時間保證只有一臺機(jī)器的一個線程可以持有鎖。
2、不能發(fā)生死鎖,無論何時持有鎖的機(jī)器崩潰掛掉了都要能自動釋放鎖。
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)識(保證鎖不會被其他人釋放)
 * @param expireTime 鎖的過期時間(單位:秒)
 * @return
 */
public boolean lock(String lockKey, String identity, long expireTime){
  boolean lockResult = redisTemplate.opsForValue().setIfAbsent(lockKey, identity, expireTime, TimeUnit.SECONDS);
  return opsForValue;
}

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

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

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

解鎖代碼

代碼如下:

/**
 * 釋放鎖
 * @param lockKey 鎖
 * @param identity 身份標(biāo)識(保證鎖不會被其他人釋放)
 * @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;
}

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

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

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

測試代碼

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;

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

  private final String lockKeyName = "testKey";

  @Autowired
  private RedisLock redisLock;

  /**
   * 測試加鎖
   * @param id 加鎖的資源id
   * @param identity 身份標(biāo)識
   * @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;
  }

  /**
   * 測試釋放鎖
   * @param id 釋放鎖的資源id
   * @param identity 身份標(biāo)識
   * @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)識(保證鎖不會被其他人釋放)
   * @param expireTime 鎖的過期時間(單位:秒)
   * @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)識(保證鎖不會被其他人釋放)
   * @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é)語

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

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

相關(guān)文章

  • Java學(xué)習(xí)筆記之異常處理

    Java學(xué)習(xí)筆記之異常處理

    這篇文章主要為大家詳細(xì)介紹了Java學(xué)習(xí)筆記之異常處理,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • MyBatis注解方式之@Update/@Delete使用詳解

    MyBatis注解方式之@Update/@Delete使用詳解

    這篇文章主要介紹了MyBatis注解方式之@Update/@Delete使用詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • java設(shè)計模式之單例模式的詳解及優(yōu)點(diǎn)

    java設(shè)計模式之單例模式的詳解及優(yōu)點(diǎn)

    這篇文章主要介紹了java設(shè)計模式之單例模式的詳解及優(yōu)點(diǎn)的相關(guān)資料,如果一個類始終只能創(chuàng)建一個實(shí)例,那么這個類被稱為單例類,這種設(shè)計模式被稱為單例模式,需要的朋友可以參考下
    2017-08-08
  • Java數(shù)組集合的深度復(fù)制代碼實(shí)例

    Java數(shù)組集合的深度復(fù)制代碼實(shí)例

    這篇文章主要介紹了Java數(shù)組集合的深度復(fù)制代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-11-11
  • Java:泛型知識知多少

    Java:泛型知識知多少

    這篇文章主要介紹了java泛型基礎(chǔ)知識及通用方法,從以下幾個方面介紹一下java的泛型: 基礎(chǔ), 泛型關(guān)鍵字, 泛型方法, 泛型類和接口,感興趣的可以了解一下
    2021-08-08
  • SpringBoot容器的主要組件詳解

    SpringBoot容器的主要組件詳解

    這篇文章主要介紹了SpringBoot容器的主要組件詳解,SpringBoot?是基于?Spring?Framework?的一種快速開發(fā)框架,它可以幫助開發(fā)者快速地構(gòu)建獨(dú)立的、生產(chǎn)級別的、可部署的應(yīng)用程序,需要的朋友可以參考下
    2023-09-09
  • 快速搭建一個SpringBoot項目(純小白搭建教程)

    快速搭建一個SpringBoot項目(純小白搭建教程)

    本文主要介紹了快速搭建一個SpringBoot項目,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • java實(shí)現(xiàn)希爾排序算法

    java實(shí)現(xiàn)希爾排序算法

    希爾排序(Shell Sort)是插入排序的一種,是針對直接插入排序算法的改進(jìn),是將整個無序列分割成若干小的子序列分別進(jìn)行插入排序,希爾排序并不穩(wěn)定。該方法又稱縮小增量排序,因DL.Shell于1959年提出而得名。
    2015-04-04
  • java 畫pdf用itext調(diào)整表格寬度、自定義各個列寬的方法

    java 畫pdf用itext調(diào)整表格寬度、自定義各個列寬的方法

    這篇文章主要介紹了java 畫pdf用itext調(diào)整表格寬度、自定義各個列寬的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • SpringCloud實(shí)現(xiàn)文件上傳功能的方法詳解

    SpringCloud實(shí)現(xiàn)文件上傳功能的方法詳解

    這篇文章主要為大家詳細(xì)介紹了SpringCloud如何實(shí)現(xiàn)文件上傳功能,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)有一定的借鑒價值,需要的可以參考一下
    2022-08-08

最新評論