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

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

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

分布式鎖實現(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>

說明:本文采用jedis來實現(xiàn)分布式鎖。

封裝工具類

@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;
    /**
     * 加鎖方法僅針對單實例 Redis,哨兵、集群模式無法使用
     *
     * @param lockKey 加鎖鍵
     * @param clientId 加鎖客戶端唯一標識(采用UUID)
     * @param seconds 鎖過期時間
     * @return true標識加鎖成功、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;
    }
}

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

模擬秒殺扣減庫存

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("秒殺成功,剩余庫存:{}",stockNum);
               }
               else
               {
                  logger.error("秒殺失敗,剩余庫存:{}", stockNum);
               }
               //獲取庫存數(shù)量
               return stockNum;
            }
            else
            {
                logger.error("加鎖失?。篶lientId:{}",clientId);
            }
        }
        catch (Exception e)
        {
           logger.error("decry stock eror",e);
        }
        finally
        {
           redisLockUtil.unLock(lockKey, clientId);
        }
        return 0;
    }

測試代碼

@RequestMapping("/redisLockTest")
    public void redisLockTest()
    {
        // 初始化秒殺庫存數(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("庫存剩余數(shù)量:{}",stockNum);
            }
            catch (Exception e)
            {
               logger.error("get stock num error",e);
            }
        });
    }

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

方案優(yōu)化

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

問題1:扣減庫存邏輯無法保證原子性,

具體的代碼如下:

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)介紹了具體的實現(xiàn)方案,可以采用lua腳本和Redis的incry原子性命令實現(xiàn),這里采用lua腳本來實現(xiàn)原子性的庫存扣減。

具體實現(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();
               // 單機模式
               if (nativeConnection instanceof Jedis) 
               {
                   return (Long) ((Jedis) nativeConnection).eval(lua_surplusStock.toString(), keys, args);
               }
               return -1l;
           }
       });
       return result;
   }

問題2:如果加鎖失敗,則會直接訪問,無法重入鎖

因為單機版本的鎖是無法重入鎖,所以加鎖失敗就直接返回,此問題的解決方案,可以采用Redisson來實現(xiàn),關(guān)于Redisson實現(xiàn)分布式鎖,將在后續(xù)的文章中進行詳細的講解。

總結(jié)

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

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

相關(guān)文章

  • Java中的CurrentHashMap源碼詳解

    Java中的CurrentHashMap源碼詳解

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

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

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

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

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

    spring.datasource.schema配置詳解

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

    Java super和this的對比及使用

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

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

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

    SpringBoot之LogBack配置詳解

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

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

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

    Java異常處理運行時異常(RuntimeException)詳解及實例

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

    springboot中如何配置LocalDateTime JSON返回時間戳

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

最新評論

息烽县| 赣榆县| 黎城县| 金湖县| 潍坊市| 海阳市| 万宁市| 江北区| 大连市| 曲麻莱县| 湘乡市| 孟州市| 福鼎市| 庆城县| 醴陵市| 贺兰县| 青阳县| 马龙县| 邯郸市| 鱼台县| 巴林左旗| 彭山县| 清镇市| 比如县| 三河市| 嘉兴市| 景东| 黔东| 侯马市| 丹东市| 沛县| 卫辉市| 芜湖县| 丽水市| 调兵山市| 永新县| 广灵县| 桦川县| 托里县| 同心县| 夹江县|