摘要:分布式鎖基于實現(xiàn)分布式鎖思考幾個問題鎖為什么不能應用于分布式鎖雖然能夠解決同步問題,但是每次只有一個線程訪問,并且鎖屬于鎖,僅適用于單點部署然而分布式需要部署多臺實例,屬于不同的線程對象使用中實現(xiàn)分布式鎖。
分布式鎖
基于redis實現(xiàn)分布式鎖思考幾個問題?
synchronized鎖為什么不能應用于分布式鎖?
synchronized雖然能夠解決同步問題,但是每次只有一個線程訪問,并且synchronized鎖屬于JVM鎖,僅適用于單點部署;然而分布式需要部署多臺實例,屬于不同的JVM線程對象
使用redis中setnx實現(xiàn)分布式鎖。
//設(shè)置分布式鎖
String lockKey = "product_001_key";
//語義:如何不存在則存入緩存中,且返回true;
//否則已存在,則返回false即加鎖失敗
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "product_001_lock");
if (!result) {
//沒有加鎖成功,則返回提示等
}
try{
}catch() {
}finally{
//釋放鎖
stringRedisTemplate.delete(lockKey);
}
針對以上設(shè)置分布式鎖思考一下問題?
1.如果突然服務器宕機,那么必然造成鎖無法釋放,即造成死鎖?
解決方案:設(shè)置超時時間。
//設(shè)置分布式鎖
String lockKey = "product_001_key";
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "product_001_lock");
//設(shè)置鎖超時時間30s
stringRedisTemplate.expire(lockKey,30, TimeUnit.SECONDS);
if (!result) {
//沒有加鎖成功,則返回提示等
}
try{
}catch() {
}finally{
//釋放鎖
stringRedisTemplate.delete(lockKey);
}
2.加鎖和設(shè)置超時時間中間引起服務器宕機,則一樣會導致死鎖。
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "product_001_lock");
//------服務器宕機,則超時時間未設(shè)置成功-------
//設(shè)置鎖超時時間30s
stringRedisTemplate.expire(lockKey,30, TimeUnit.SECONDS);
解決方案:原子性操作,即同時加鎖和設(shè)置超時時間;
即上面的代碼合并成一句操作:
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"product_001_lock", 30, TimeUnit.SECONDS)
3.思考超時時間設(shè)置是否合理呢?即線程執(zhí)行時間和鎖超時時間并非一致。
場景:假設(shè)設(shè)置加鎖超時時間10s;
高并發(fā)場景下,線程A執(zhí)行時間為15s,redis依據(jù)超時時間,將其線程A加的鎖釋放掉;然后線程B獲取鎖,并加鎖成功,此時線程A執(zhí)行結(jié)束,執(zhí)行finally代碼塊就會將線程B加的鎖釋放。
解決方案:設(shè)置線程隨機ID,釋放鎖時判斷是否為當前線程加的鎖,即使存在線程A因線程執(zhí)行時間超時被動釋放其鎖,但至少保證當前超時線程不會釋放其他線程加的鎖。但是面對線程執(zhí)行時間大于設(shè)置的超時時間,也是會存在并發(fā)問題。
String lockKey = "product_001";
String clientId = UUID.randomUUID().toString();
//設(shè)置超時時間,且加鎖和設(shè)置線程ID
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,clientId, 30, TimeUnit.SECONDS)`
if (!result) {
//沒有加鎖成功,則返回提示等
}
try{
}catch() {
}finally{
//釋放鎖:加鎖線程ID和當前執(zhí)行線程ID相同,才允許釋放鎖
if (clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))){
stringRedisTemplate.delete(lockKey);
}
}
4.上面場景解決方案:加鎖續(xù)命即續(xù)線程鎖超時時間
解決方案:加鎖成功時,開啟一個后臺線程,每隔10s(自定義)判斷當前線程是否還持有鎖,持有鎖則再續(xù)命30s等
Redission實現(xiàn)分布式鎖
實現(xiàn)原理流程:
String lockKey = "product_001";
//獲取鎖對象,并未加鎖
RLock redissonLock = redisson.getLock(lockKey);
try {
// **此時加鎖**,實現(xiàn)鎖續(xù)命功能
redissonLock.lock();
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("扣減成功,剩余庫存:" + realStock + "");
} else {
System.out.println("扣減失敗,庫存不足");
}
}finally {
//釋放鎖
redissonLock.unlock();
}
總結(jié)
綜上,設(shè)計實現(xiàn)分布式鎖需要滿足一下條件:
1.互斥性;在任意時刻,只有一個客戶端能持有鎖。
2.不能發(fā)生死鎖;即使存在一個線程持有鎖的期間崩潰而沒有主動解鎖,也能保證后續(xù)其他線程能加鎖。
3.加鎖和解鎖必須是同一個線程。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/125953.html
摘要:作為面試官,我是如何甄別應聘者的包裝程度語言和等其他語言的對比分析和主從復制的原理詳解和持久化的原理是什么面試中經(jīng)常被問到的持久化與恢復實現(xiàn)故障恢復自動化詳解哨兵技術(shù)查漏補缺最易錯過的技術(shù)要點大掃盲意外宕機不難解決,但你真的懂數(shù)據(jù)恢復嗎每秒 作為面試官,我是如何甄別應聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復制的原理詳...
摘要:作為面試官,我是如何甄別應聘者的包裝程度語言和等其他語言的對比分析和主從復制的原理詳解和持久化的原理是什么面試中經(jīng)常被問到的持久化與恢復實現(xiàn)故障恢復自動化詳解哨兵技術(shù)查漏補缺最易錯過的技術(shù)要點大掃盲意外宕機不難解決,但你真的懂數(shù)據(jù)恢復嗎每秒 作為面試官,我是如何甄別應聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復制的原理詳...
閱讀 3580·2023-04-25 20:09
閱讀 3770·2022-06-28 19:00
閱讀 3115·2022-06-28 19:00
閱讀 3129·2022-06-28 19:00
閱讀 3230·2022-06-28 19:00
閱讀 2917·2022-06-28 19:00
閱讀 3104·2022-06-28 19:00
閱讀 2703·2022-06-28 19:00