Redisson分布式锁实战(适用于Redis高并发场景)
實現方式一:存在拋異常后lock值無法歸0的問題
@Autowired private StringRedisTemplate stringRedisTemplate;@RequestMapping("deduct_stock")public void deductStock(){Long num = stringRedisTemplate.opsForValue().increment("lock", 1);//多個線程過來 只有一個線程會將num值設置為1 其余的線程都不可能為1if (num==1){int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));if (stock>0){stringRedisTemplate.opsForValue().set("stock",(stock-1)+"");System.out.println("扣減成功,庫存stock:"+(stock-1)+"");}else{System.out.println("扣減失敗,庫存不足");}}//還原stringRedisTemplate.opsForValue().increment("lock",-1);}實現方式二:加try…finally。無論程序是否拋出異常,最終都會還原。但是在還原為0的時候有可能redis掛了,或者是程序還沒執行完try代碼塊中的內容,整個web應用掛掉了,finally塊中的內容也無法執行。即使程序重啟,后面的線程也始終無法滿足num==1的條件。
@Autowired private StringRedisTemplate stringRedisTemplate;@RequestMapping("deduct_stock") public void deductStock(){try{Long num = stringRedisTemplate.opsForValue().increment("lock", 1);//多個線程過來 只有一個線程會將num值設置為1 其余的線程都不可能為1if (num==1){int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));if (stock>0){stringRedisTemplate.opsForValue().set("stock",(stock-1)+"");System.out.println("扣減成功,庫存stock:"+(stock-1)+"");}else{System.out.println("扣減失敗,庫存不足");}}}finally {//還原stringRedisTemplate.opsForValue().increment("lock",-1);} }實現方式三:設置lock的超時時間。存在這么一個場景,由于某一段時間內,網絡環境差,導致本應10秒內就執行完的程序執行了15秒才完成,而鎖已經過期了,也就意味著后面的線程能拿到鎖,導致鎖形同虛設。
還存在這么一個情況:第一個線程執行時長15秒,假設第二個線程執行時長8秒,由于鎖已失效,第二個線程是能重新拿到新鎖的,結果第一個線程在執行歸0操作釋放鎖時,它自己的鎖已失效,導致釋放的并不是自己的鎖,而是第二個線程的鎖。總之,存在一系列場景導致鎖失效。
這種場景下,可以開啟一個分線程,與當前線程綁定,如果鎖的失效時間是10秒,那么就每隔5秒去掃描一下這把鎖,看看鎖是否還在,如果還在就再次將失效時間重置為10秒,不斷延時,也就相當于讓這把鎖具備了自動延時的功能,直到當前線程親自將這把鎖釋放,否則一致延時下去。
Redisson框架實現分布式鎖
上述分析也就是Redisson的實現原理,只不過會增加一個線程,讓等待的線程不斷地嘗試加鎖,通過while循環來實現的,俗稱就是自旋加鎖。
配置單機Redis
/*** @ProjectName springbootdemo_src* @ClassName RedissionConfig* @Desicription TODO* @Author Zhang Xueliang* @Date 2019/7/27 17:34* @Version 1.0**/ @Configuration public class RedissonConfig {@Bean("redisson")//如果不寫value 默認就會以方法名作為bean的名稱進行注入public Redisson getRedisson(){Config config = new Config();config.useSingleServer().setAddress("redis://127.0.0.1:6379").setDatabase(0);return (Redisson) Redisson.create(config);} }加鎖代碼:很簡單,就三行代碼。重要的是理解運行原理。
/*** @ProjectName springbootdemo_src* @ClassName RedissionController* @Desicription TODO* @Author Zhang Xueliang* @Date 2019/7/27 15:55* @Version 1.0**/@SuppressWarnings("all") @RestController public class RedissionController {@Autowiredprivate Redisson redisson;@RequestMapping("redisson_lock")public void redissonDeductStock() {String lockkey = "lock";RLock lock = redisson.getLock(lockkey);try {lock.lock();//如果使用默認的午餐lock方法 默認超時時間設置的是30秒 可以傳入自定義的超時時間int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));if (stock > 0) {stringRedisTemplate.opsForValue().set("stock", (stock - 1) + "");System.out.println("扣減成功,庫存stock:" + (stock - 1) + "");} else {System.out.println("扣減失敗,庫存不足");}} finally {lock.unlock();}}}Redisson實質上就是給代碼加上了一把悲觀鎖,而悲觀鎖是比較影響性能的。increment自增加1的方式實質是樂觀鎖。
Redis天生就是單線程的,在高并發環境主從集群還是會存在一定問題。
總結
以上是生活随笔為你收集整理的Redisson分布式锁实战(适用于Redis高并发场景)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TX-LCN分布式事务框架开发文档
- 下一篇: Redis缓存高可用集群哨兵模式详解