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

Spring?Boot?實(shí)現(xiàn)Redis分布式鎖原理

 更新時(shí)間:2022年08月04日 09:37:42   作者:劍圣無(wú)痕  
這篇文章主要介紹了Spring?Boot實(shí)現(xiàn)Redis分布式鎖原理,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下

分布式鎖實(shí)現(xiàn)

引入jar包

<dependency>
 <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
        <exclusions>
   <exclusion>
 <groupId>io.lettuce</groupId>
 <artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
 <dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
 </dependency>

說(shuō)明:本文采用jedis來(lái)實(shí)現(xiàn)分布式鎖。

封裝工具類(lèi)

@Component
public class RedisLockUtil
{
    private static final Logger logger = LoggerFactory.getLogger(RedisLockUtil.class);
    private static final Long RELEASE_SUCCESS = 1L;
    private static final String LOCK_SUCCESS = "OK";
    private static final String RELEASE_LOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 加鎖方法僅針對(duì)單實(shí)例 Redis,哨兵、集群模式無(wú)法使用
     *
     * @param lockKey 加鎖鍵
     * @param clientId 加鎖客戶(hù)端唯一標(biāo)識(shí)(采用UUID)
     * @param seconds 鎖過(guò)期時(shí)間
     * @return true標(biāo)識(shí)加鎖成功、false代表加鎖失敗
     */
    public Boolean tryLock(String lockKey, String clientId, long seconds)
    {
        try
        {
            return redisTemplate
                    .execute((RedisCallback<Boolean>) redisConnection -> {
                        Jedis jedis = (Jedis) redisConnection.getNativeConnection();
                        SetParams params =new SetParams();
                        params.nx();
                        params.px(seconds);
                        String result = jedis.set(lockKey, clientId, params);
                        if (LOCK_SUCCESS.equals(result))
                        {
                            return Boolean.TRUE;
                        }
                        return Boolean.FALSE;
                    });
        }
        catch (Exception e)
        {
            logger.error("tryLock error",e);
        }

        return false;
    }
    /**
     *釋放鎖,保持原子性操作,采用了lua腳本
     *
     * @param lockKey
     * @param clientId
     * @return
     */
    public Boolean unLock(String lockKey, String clientId)
    {
        try
        {
            return  redisTemplate
                    .execute((RedisCallback<Boolean>) redisConnection -> {
                        Jedis jedis = (Jedis) redisConnection.getNativeConnection();
                        Object result = jedis.eval(RELEASE_LOCK_SCRIPT,
                                Collections.singletonList(lockKey),
                                Collections.singletonList(clientId));
                        if (RELEASE_SUCCESS.equals(result))
                        {
                            return Boolean.TRUE;
                        }
                        return Boolean.FALSE;
                    });
        }
        catch (Exception e)
        {
            logger.error("unlock error",e);
        }
        return Boolean.FALSE;
    }
}

說(shuō)明:加鎖的原理是基于Redis的NX、PX命令,而解鎖采用的是lua腳本實(shí)現(xiàn)。

模擬秒殺扣減庫(kù)存

public int lockStock()
    {
        String lockKey="lock:stock";
        String clientId = UUID.randomUUID().toString();
        long seconds =1000l;

        try
        {
            //加鎖
            boolean flag=redisLockUtil.tryLock(lockKey, clientId, seconds);
            //加鎖成功
            if(flag)
            {
               logger.info("加鎖成功 clientId:{}",clientId);
               int stockNum= Integer.valueOf((String)redisUtil.get("seckill:goods:stock"));
               if(stockNum>0)
               {
                  stockNum--;
                  redisUtil.set("seckill:goods:stock",String.valueOf(stockNum));
                  logger.info("秒殺成功,剩余庫(kù)存:{}",stockNum);
               }
               else
               {
                  logger.error("秒殺失敗,剩余庫(kù)存:{}", stockNum);
               }
               //獲取庫(kù)存數(shù)量
               return stockNum;
            }
            else
            {
                logger.error("加鎖失敗:clientId:{}",clientId);
            }
        }
        catch (Exception e)
        {
           logger.error("decry stock eror",e);
        }
        finally
        {
           redisLockUtil.unLock(lockKey, clientId);
        }
        return 0;
    }

測(cè)試代碼

@RequestMapping("/redisLockTest")
    public void redisLockTest()
    {
        // 初始化秒殺庫(kù)存數(shù)量
        redisUtil.set("seckill:goods:stock", "10");

        List<Future> futureList = new ArrayList<>();

        //多線程異步執(zhí)行
        ExecutorService executors = Executors.newScheduledThreadPool(10);
        //
        for (int i = 0; i < 30; i++)
        {
            futureList.add(executors.submit(this::lockStock));

            try
            {
               Thread.sleep(100);
            }
            catch (InterruptedException e) 
            {
               logger.error("redisLockTest error",e);
            }
        }

        // 等待結(jié)果,防止主線程退出
        futureList.forEach(t -> {
            try 
            {
                int stockNum =(int) t.get();
                logger.info("庫(kù)存剩余數(shù)量:{}",stockNum);
            }
            catch (Exception e)
            {
               logger.error("get stock num error",e);
            }
        });
    }

執(zhí)行結(jié)果如下:

方案優(yōu)化

上述分布式鎖實(shí)現(xiàn)庫(kù)存扣減是否存在相關(guān)問(wèn)題呢?

問(wèn)題1:扣減庫(kù)存邏輯無(wú)法保證原子性,

具體的代碼如下:

int stockNum= Integer.valueOf((String)redisUtil.get("seckill:goods:stock"));
if(stockNum>0)
 {
    stockNum--;
    redisUtil.set("seckill:goods:stock",String.valueOf(stockNum));
 }

這是典型的RMW模型,前面章節(jié)已經(jīng)介紹了具體的實(shí)現(xiàn)方案,可以采用lua腳本和Redis的incry原子性命令實(shí)現(xiàn),這里采用lua腳本來(lái)實(shí)現(xiàn)原子性的庫(kù)存扣減。

具體實(shí)現(xiàn)如下:

  public long surplusStock(String key ,int num)
   {
       StringBuilder lua_surplusStock = new StringBuilder();
       lua_surplusStock.append("   local key = KEYS[1];");
       lua_surplusStock.append("   local subNum = tonumber(ARGV[1]);");
       lua_surplusStock.append("   local surplusStock=tonumber(redis.call('get',key));");
       lua_surplusStock.append("    if (surplusStock- subNum>= -1) then");
       lua_surplusStock.append("        return redis.call('incrby', KEYS[1], 0-subNum);");
       lua_surplusStock.append("    else ");
       lua_surplusStock.append("    return -1;");
       lua_surplusStock.append("    end");
       
       List<String> keys = new ArrayList<>();
       keys.add(key);
       // 腳本里的ARGV參數(shù)
       List<String> args = new ArrayList<>();
       args.add(Integer.toString(num));

       long result = redisTemplate.execute(new RedisCallback<Long>() {
           @Override
           public Long doInRedis(RedisConnection connection) throws DataAccessException {
               Object nativeConnection = connection.getNativeConnection();
               // 單機(jī)模式
               if (nativeConnection instanceof Jedis) 
               {
                   return (Long) ((Jedis) nativeConnection).eval(lua_surplusStock.toString(), keys, args);
               }
               return -1l;
           }
       });
       return result;
   }

問(wèn)題2:如果加鎖失敗,則會(huì)直接訪問(wèn),無(wú)法重入鎖

因?yàn)閱螜C(jī)版本的鎖是無(wú)法重入鎖,所以加鎖失敗就直接返回,此問(wèn)題的解決方案,可以采用Redisson來(lái)實(shí)現(xiàn),關(guān)于Redisson實(shí)現(xiàn)分布式鎖,將在后續(xù)的文章中進(jìn)行詳細(xì)的講解。

總結(jié)

本文主要講解了Spring Boot集成Redis實(shí)現(xiàn)單機(jī)版本分布式鎖,雖然單機(jī)版分布式鎖存在鎖的續(xù)期、鎖的重入問(wèn)題,但是我們還是需要掌握其原理和實(shí)現(xiàn)方法

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

相關(guān)文章

  • Java中的CurrentHashMap源碼詳解

    Java中的CurrentHashMap源碼詳解

    這篇文章主要介紹了Java中的CurrentHashMap源碼詳解,HashMap是數(shù)組+鏈表構(gòu)成的,JDK1.8之后,加入了紅黑樹(shù),HashMap默認(rèn)數(shù)組初始化大小為16,如果瞎設(shè)置數(shù)字,它會(huì)自動(dòng)調(diào)整成2的倍數(shù),需要的朋友可以參考下
    2023-12-12
  • Java異常架構(gòu)Exception(異常)詳解

    Java異常架構(gòu)Exception(異常)詳解

    這篇文章主要介紹了Java異常架構(gòu)Exception(異常),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • java實(shí)現(xiàn)自定義表格渲染和編輯

    java實(shí)現(xiàn)自定義表格渲染和編輯

    這篇文章主要為大家詳細(xì)介紹了java如何實(shí)現(xiàn)自定義表格渲染和編輯,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-04-04
  • spring.datasource.schema配置詳解

    spring.datasource.schema配置詳解

    本文主要介紹了spring.datasource.schema配置,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Java super和this的對(duì)比及使用

    Java super和this的對(duì)比及使用

    這篇文章主要介紹了Java super和this的對(duì)比及使用的相關(guān)資料,java中this與super會(huì)經(jīng)常在使用的時(shí)候混淆,需要的朋友可以參考下
    2017-08-08
  • 深入解析Java的線程同步以及線程間通信

    深入解析Java的線程同步以及線程間通信

    這篇文章主要介紹了Java的線程同步以及線程間通信,多線程編程是Java學(xué)習(xí)中的重點(diǎn)和難點(diǎn),需要的朋友可以參考下
    2015-09-09
  • SpringBoot之LogBack配置詳解

    SpringBoot之LogBack配置詳解

    這篇文章主要介紹了SpringBoot之LogBack配置詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-02-02
  • java 自己實(shí)現(xiàn)DataSource實(shí)現(xiàn)實(shí)例

    java 自己實(shí)現(xiàn)DataSource實(shí)現(xiàn)實(shí)例

    這篇文章主要介紹了java 自己實(shí)現(xiàn)DataSource實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Java異常處理運(yùn)行時(shí)異常(RuntimeException)詳解及實(shí)例

    Java異常處理運(yùn)行時(shí)異常(RuntimeException)詳解及實(shí)例

    這篇文章主要介紹了 Java異常處理運(yùn)行時(shí)異常(RuntimeException)詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下http://time.qq.com/?pgv_ref=aiotime
    2017-05-05
  • springboot中如何配置LocalDateTime JSON返回時(shí)間戳

    springboot中如何配置LocalDateTime JSON返回時(shí)間戳

    這篇文章主要介紹了springboot中如何配置LocalDateTime JSON返回時(shí)間戳問(wèn)題。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06

最新評(píng)論

江门市| 红桥区| 淮南市| 孟村| 江华| 青海省| 广德县| 杭锦后旗| 水城县| 南昌县| 肥东县| 鄂托克前旗| 甘洛县| 集贤县| 沾化县| 北安市| 文登市| 浪卡子县| 宣化县| 永丰县| 汕尾市| 宣汉县| 威信县| 松阳县| 台前县| 阿克陶县| 塔河县| 德令哈市| 武山县| 柘荣县| 阜城县| 达拉特旗| 枞阳县| 绥芬河市| 平昌县| 库伦旗| 陆丰市| 南召县| 伊通| 保靖县| 商丘市|