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