溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么使用RedisTemplat實(shí)現(xiàn)簡(jiǎn)單的分布式鎖

發(fā)布時(shí)間:2021-11-19 16:25:49 來(lái)源:億速云 閱讀:109 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要講解了“怎么使用RedisTemplat實(shí)現(xiàn)簡(jiǎn)單的分布式鎖”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“怎么使用RedisTemplat實(shí)現(xiàn)簡(jiǎn)單的分布式鎖”吧!

不使用redisson框架實(shí)現(xiàn)Redis分布式鎖 準(zhǔn)備工作:

導(dǎo)入依賴

<dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-data-redis</artifactId></dependency>

編寫(xiě)RedisConfig類(lèi)

@Configurationpublic class RedisConfig {    @Bean    public RedisTemplate<String , Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();        //String類(lèi)型 key序列器        redisTemplate.setKeySerializer(new StringRedisSerializer());        //String類(lèi)型 value序列器        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());        //Hash類(lèi)型 key序列器        redisTemplate.setHashKeySerializer(new StringRedisSerializer());        //Hash類(lèi)型 value序列器        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());        //將連接工廠注入        redisTemplate.setConnectionFactory(redisConnectionFactory);        return redisTemplate;    } }

1.在SpringBootTest中編寫(xiě)測(cè)試模塊 1.1:使用占位符加鎖:

占位符加鎖問(wèn)題:出現(xiàn)異常時(shí)無(wú)法釋放鎖,導(dǎo)致后繼進(jìn)入的線程成為死鎖

@SpringBootTestclass ApplicationTests {    @Autowired    private RedisTemplate redisTemplate;@Testpublic void lodsTest01(){ValueOperations valueOperations = redisTemplate.opsForValue();        //創(chuàng)建一個(gè)占位符,如果key不存在才可以設(shè)置成功        Boolean isLock = valueOperations.setIfAbsent("k1", "v1");        //如果占位成功,進(jìn)行正常操作        if (isLock){        //設(shè)置一個(gè)name存到redis            valueOperations.set("name","xxxx");            //從redis取出name            String name = (String) valueOperations.get("name");            System.out.println("name = " + name);            //手動(dòng)制造異常            Integer.parseInt("xxxx");            //操作結(jié)束刪除鎖            redisTemplate.delete("k1");        }else{            System.out.println("有線程在用,請(qǐng)稍后在試");        }}}

測(cè)試
第一個(gè)線程出現(xiàn)異常無(wú)法釋放鎖:
怎么使用RedisTemplat實(shí)現(xiàn)簡(jiǎn)單的分布式鎖
之后所有線程都無(wú)法訪問(wèn):
怎么使用RedisTemplat實(shí)現(xiàn)簡(jiǎn)單的分布式鎖

解決方案為鎖加一個(gè)有效時(shí)間。

1.2:使用占位符設(shè)置有效時(shí)間解決死鎖問(wèn)題:

占位符設(shè)置有效時(shí)間問(wèn)題即使某線程出現(xiàn)異常,但占位符過(guò)了有效時(shí)間,鎖就會(huì)釋放。但是在大量線程同時(shí)訪問(wèn)時(shí),如果線程1被外界因素影響(網(wǎng)絡(luò)波動(dòng),服務(wù)器出問(wèn)題等等),線程1的業(yè)務(wù)還沒(méi)完成,但鎖的有效時(shí)間到了的話,下一個(gè)線程就會(huì)進(jìn)來(lái),就會(huì)出現(xiàn)線程不安全的情況,出現(xiàn)線程互相刪鎖的情況。

@Test    public void testLock02()  {        ValueOperations valueOperations = redisTemplate.opsForValue();        //如果key不存在才可以設(shè)置成功,設(shè)置一個(gè)有效時(shí)間防止線程異常出現(xiàn)死鎖        Boolean isLock = valueOperations.setIfAbsent("k1", "v1",5, TimeUnit.SECONDS);        //如果占位成功,進(jìn)行正常操作        if (isLock){        //設(shè)置一個(gè)name存到redis            valueOperations.set("name","xxxx");            //從redis取出name            String str = (String) valueOperations.get("name");            System.out.println("name = " + str);            //制造異常            Integer.parseInt("xxxx");            //操作結(jié)束刪除鎖            redisTemplate.delete("k1");        }else{            System.out.println("有線程在用,請(qǐng)稍后在試");        }    }

解決方案: 使用lua腳本,給每個(gè)鎖的key對(duì)應(yīng)的value設(shè)置一個(gè)隨機(jī)數(shù)

1.3:使用lua腳本解決線程不安全問(wèn)題:

lua腳本可以寫(xiě)在Redis服務(wù)器上:
優(yōu)點(diǎn): 在服務(wù)器上運(yùn)行速度快

缺點(diǎn): 修改代碼時(shí)比較麻煩

lua腳本可以通過(guò)java發(fā)送
優(yōu)點(diǎn): 修改代碼方便

缺點(diǎn): 每次發(fā)送請(qǐng)求時(shí)都需要占用網(wǎng)絡(luò)資源

1.3.1:編寫(xiě)lua腳本

  怎么使用RedisTemplat實(shí)現(xiàn)簡(jiǎn)單的分布式鎖

if redis.call("get",KEYS[1])==ARGV[1] then    return redis.call("del",KEYS[1])else    return 0end

1.3.2:修改ReidsConfig類(lèi)

@Bean    public DefaultRedisScript<Boolean> defaultRedisScript(){        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();        //lock.lua腳本位置和application.yml同級(jí)目錄        redisScript.setLocation(new ClassPathResource("lock.lua"));        //設(shè)置類(lèi)型為boolean        redisScript.setResultType(Boolean.class);        return redisScript;    }

1.3.3:編寫(xiě)測(cè)試模塊

@Test    public void testLock03(){        ValueOperations valueOperations = redisTemplate.opsForValue();        String value = UUID.randomUUID().toString();        //如果key不存在才可以設(shè)置成功,設(shè)置一個(gè)value為隨機(jī)數(shù)的值,防止出現(xiàn)線程太多 導(dǎo)致線程不安全        Boolean isLock = valueOperations.setIfAbsent("k1", value, 5, TimeUnit.SECONDS);        //如果占位成功,進(jìn)行正常操作         if (isLock){        //設(shè)置一個(gè)name存到redis            valueOperations.set("name","xxxx");            //從redis取出name            String name = (String) valueOperations.get("name");            System.out.println("name = " + name);            //為redis發(fā)送lua腳本刪除鎖對(duì)應(yīng)的value            Boolean aBoolean = (Boolean) redisTemplate.execute(redisScript, Collections.singletonList("k1"), value);            System.out.println(aBoolean);        }else{            System.out.println("有線程在用,請(qǐng)稍后在試");        }    }

測(cè)試結(jié)果:
順利把name值存到redis中并把鎖刪除并返回true
怎么使用RedisTemplat實(shí)現(xiàn)簡(jiǎn)單的分布式鎖
鎖會(huì)被正常刪除只留下name:
怎么使用RedisTemplat實(shí)現(xiàn)簡(jiǎn)單的分布式鎖

感謝各位的閱讀,以上就是“怎么使用RedisTemplat實(shí)現(xiàn)簡(jiǎn)單的分布式鎖”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)怎么使用RedisTemplat實(shí)現(xiàn)簡(jiǎn)單的分布式鎖這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI