這篇文章主要介紹了redis發(fā)生死鎖問(wèn)題怎么辦,具有一定借鑒價(jià)值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。
創(chuàng)新互聯(lián)公司專(zhuān)注為客戶(hù)提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都網(wǎng)站制作、成都網(wǎng)站建設(shè)、花山網(wǎng)絡(luò)推廣、成都小程序開(kāi)發(fā)、花山網(wǎng)絡(luò)營(yíng)銷(xiāo)、花山企業(yè)策劃、花山品牌公關(guān)、搜索引擎seo、人物專(zhuān)訪、企業(yè)宣傳片、企業(yè)代運(yùn)營(yíng)等,從售前售中售后,我們都將竭誠(chéng)為您服務(wù),您的肯定,是我們最大的嘉獎(jiǎng);創(chuàng)新互聯(lián)公司為所有大學(xué)生創(chuàng)業(yè)者提供花山建站搭建服務(wù),24小時(shí)服務(wù)熱線:18980820575,官方網(wǎng)址:www.cdcxhl.com
就分布式鎖而言,一個(gè)常用的問(wèn)題就是如果一個(gè)服務(wù)setnx成功了,但是在解鎖的時(shí)候如果發(fā)生了宕機(jī)或者一些特殊因素,導(dǎo)致無(wú)法解鎖,那么其他服務(wù)將陷入死鎖的狀態(tài)。所以,我們?cè)谟?setnx 的同時(shí)想著去用 expire 指令對(duì)鎖進(jìn)行一個(gè)過(guò)期操作, 從指令可以看出 setnx 和expire指令是分開(kāi)的,如果在這中間的空隙過(guò)程中如果有特殊因素導(dǎo)致指令無(wú)法繼續(xù),也會(huì)導(dǎo)致死鎖的產(chǎn)生。
解決方法:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; @Component public class RedisLock { Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private StringRedisTemplate redisTemplate; /** * 加鎖 * @param key * @param value 當(dāng)前時(shí)間 + 超時(shí)時(shí)間 * @return */ public boolean lock(String key, String value) { if (redisTemplate.opsForValue().setIfAbsent(key, value)) { // 這個(gè)其實(shí)就是setnx命令,只不過(guò)在java這邊稍有變化,返回的是boolean // 設(shè)置個(gè)過(guò)期時(shí)間,當(dāng)然如果在這中間的空隙過(guò)程中如果有特殊因素導(dǎo)致指令無(wú)法繼續(xù),也會(huì)導(dǎo)致死鎖的產(chǎn)生,如果死鎖出現(xiàn),則后續(xù)代碼會(huì)處理 redisTemplate.expire(key, lockTime, TimeUnit.SECONDS); return true; } // 避免死鎖,且只讓一個(gè)線程拿到鎖 String currentValue = redisTemplate.opsForValue().get(key); // 如果鎖過(guò)期了 if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) { //獲取上一個(gè)鎖的時(shí)間 String oldValues = redisTemplate.opsForValue().getAndSet(key, value); /* 只會(huì)讓一個(gè)線程拿到鎖 如果舊的value和currentValue相等,只會(huì)有一個(gè)線程達(dá)成條件,因?yàn)榈诙€(gè)線程拿到的oldValue已經(jīng)和currentValue不一樣了 */ if (!StringUtils.isEmpty(oldValues) && oldValues.equals(currentValue)) { return true; } } return false; } /** * 解鎖 * @param key * @param value */ public void unlock(String key, String value) { try { String currentValue = redisTemplate.opsForValue().get(key); if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) { redisTemplate.opsForValue().getOperations().delete(key); } } catch (Exception e) { logger.error("redis分布式鎖解鎖異常,{}", e); } } }
調(diào)用:
//加鎖 long time = System.currentTimeMillis() + 1000 * lockTime //超時(shí)時(shí)間:10秒,最好設(shè)為常量 boolean isLock = redisLock.lock(...keyName, String.valueOf(time)); if(!isLock){ throw new RuntimeException("系統(tǒng)正忙"); } // doSomething... //解鎖 redisLock.unlock(...keyName, String.valueOf(time));
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享redis發(fā)生死鎖問(wèn)題怎么辦內(nèi)容對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,遇到問(wèn)題就找創(chuàng)新互聯(lián),詳細(xì)的解決方法等著你來(lái)學(xué)習(xí)!