【110期】面試官:Redis分布式鎖如何解決鎖超時(shí)問(wèn)題?
閱讀本文大概需要 6.5 分鐘。
來(lái)自:www.jianshu.com/p/39b3570d3b56
一、前言
二、準(zhǔn)備工作
https://pan.baidu.com/share/init?surl=NN0c0tDYQjBTTPA-WTT3yg
提取碼: 8f2a
https://pan.baidu.com/share/init?surl=NoJtZZZOXsk45aQYtveWbQ
提取碼: 9bhf
https://pan.baidu.com/share/init?surl=28sGJk4zxoOknAd-47hE7w
提取碼: vfu7
三、說(shuō)明
????????
????????<dependency>
????????????<groupId>org.springframework.datagroupId>
????????????<artifactId>spring-data-redisartifactId>
????????????<version>1.6.5.RELEASEversion>
????????dependency>
????????<dependency>
????????????<groupId>redis.clientsgroupId>
????????????<artifactId>jedisartifactId>
????????????<version>2.7.3version>
????????dependency>
分布式鎖工具類: DistributedLock
測(cè)試接口類: PcInformationServiceImpl
鎖延時(shí)守護(hù)線程類: PostponeTask
四、實(shí)現(xiàn)思路
五、實(shí)現(xiàn)
1、版本01代碼
1)、DistributedLock
package?com.cn.pinliang.common.util;
import?com.cn.pinliang.common.thread.PostponeTask;
import?com.google.common.collect.Lists;
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.data.redis.core.RedisCallback;
import?org.springframework.data.redis.core.RedisTemplate;
import?org.springframework.stereotype.Component;
import?redis.clients.jedis.Jedis;
import?java.io.Serializable;
import?java.util.Collections;
@Component
public?class?DistributedLock?{
????@Autowired
????private?RedisTemplate?redisTemplate;
????private?static?final?Long?RELEASE_SUCCESS?=?1L;
????private?static?final?String?LOCK_SUCCESS?=?"OK";
????private?static?final?String?SET_IF_NOT_EXIST?=?"NX";
????private?static?final?String?SET_WITH_EXPIRE_TIME?=?"EX";
????//?解鎖腳本(lua)
????private?static?final?String?RELEASE_LOCK_SCRIPT?=?"if?redis.call('get',?KEYS[1])?==?ARGV[1]?then?return?redis.call('del',?KEYS[1])?else?return?0?end";
????/**
?????*?分布式鎖
?????*?@param?key
?????*?@param?value
?????*?@param?expireTime?單位:?秒
?????*?@return
?????*/
????public?boolean?lock(String?key,?String?value,?long?expireTime)?{
????????return?redisTemplate.execute((RedisCallback<Boolean>)?redisConnection?->?{
????????????Jedis?jedis?=?(Jedis)?redisConnection.getNativeConnection();
????????????String?result?=?jedis.set(key,?value,?SET_IF_NOT_EXIST,?SET_WITH_EXPIRE_TIME,?expireTime);
????????????if?(LOCK_SUCCESS.equals(result))?{
????????????????return?Boolean.TRUE;
????????????}
????????????return?Boolean.FALSE;
????????});
????}
????/**
?????*?解鎖
?????*?@param?key
?????*?@param?value
?????*?@return
?????*/
????public?Boolean?unLock(String?key,?String?value)?{
????????return?redisTemplate.execute((RedisCallback<Boolean>)?redisConnection?->?{
????????????Jedis?jedis?=?(Jedis)?redisConnection.getNativeConnection();
????????????Object?result?=?jedis.eval(RELEASE_LOCK_SCRIPT,?Collections.singletonList(key),?Collections.singletonList(value));
????????????if?(RELEASE_SUCCESS.equals(result))?{
????????????????return?Boolean.TRUE;
????????????}
????????????return?Boolean.FALSE;
????????});
????}
}
2)、PcInformationServiceImpl
????public?JsonResult?add()?throws?Exception?{
????????String?key?=?"add_information_lock";
????????String?value?=?RandomUtil.produceStringAndNumber(10);
????????long?expireTime?=?10L;
????????boolean?lock?=?distributedLock.lock(key,?value,?expireTime);
????????String?threadName?=?Thread.currentThread().getName();
????????if?(lock)?{
????????????System.out.println(threadName?+?"?獲得鎖...............................");
????????????Thread.sleep(30000);
????????????distributedLock.unLock(key,?value);
????????????System.out.println(threadName?+?"?解鎖了...............................");
????????}?else?{
????????????System.out.println(threadName?+?"?未獲取到鎖...............................");
????????????return?JsonResult.fail("未獲取到鎖");
????????}
????????return?JsonResult.succeed();
????}
3)、打開(kāi)redis-desktop-manager客戶端, 刷新緩存, 可以看到, 此時(shí)是沒(méi)有add_information_lock的key的

4)、啟動(dòng)jmeter, 調(diào)用接口測(cè)試



可以看到, 操作成功, 說(shuō)明A和B同時(shí)執(zhí)行了這段本應(yīng)該獨(dú)享的代碼, 需要優(yōu)化。2、版本02代碼
1)、DistributedLock
package?com.cn.pinliang.common.util;
import?com.cn.pinliang.common.thread.PostponeTask;
import?com.google.common.collect.Lists;
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.data.redis.core.RedisCallback;
import?org.springframework.data.redis.core.RedisTemplate;
import?org.springframework.stereotype.Component;
import?redis.clients.jedis.Jedis;
import?java.io.Serializable;
import?java.util.Collections;
@Component
public?class?DistributedLock?{
????@Autowired
????private?RedisTemplate?redisTemplate;
????private?static?final?Long?RELEASE_SUCCESS?=?1L;
????private?static?final?Long?POSTPONE_SUCCESS?=?1L;
????private?static?final?String?LOCK_SUCCESS?=?"OK";
????private?static?final?String?SET_IF_NOT_EXIST?=?"NX";
????private?static?final?String?SET_WITH_EXPIRE_TIME?=?"EX";
????//?解鎖腳本(lua)
????private?static?final?String?RELEASE_LOCK_SCRIPT?=?"if?redis.call('get',?KEYS[1])?==?ARGV[1]?then?return?redis.call('del',?KEYS[1])?else?return?0?end";
????//?延時(shí)腳本
????private?static?final?String?POSTPONE_LOCK_SCRIPT?=?"if?redis.call('get',?KEYS[1])?==?ARGV[1]?then?return?redis.call('expire',?KEYS[1],?ARGV[2])?else?return?'0'?end";
????/**
?????*?分布式鎖
?????*?@param?key
?????*?@param?value
?????*?@param?expireTime?單位:?秒
?????*?@return
?????*/
????public?boolean?lock(String?key,?String?value,?long?expireTime)?{
????????//?加鎖
????????Boolean?locked?=?redisTemplate.execute((RedisCallback<Boolean>)?redisConnection?->?{
????????????Jedis?jedis?=?(Jedis)?redisConnection.getNativeConnection();
????????????String?result?=?jedis.set(key,?value,?SET_IF_NOT_EXIST,?SET_WITH_EXPIRE_TIME,?expireTime);
????????????if?(LOCK_SUCCESS.equals(result))?{
????????????????return?Boolean.TRUE;
????????????}
????????????return?Boolean.FALSE;
????????});
????????if?(locked)?{
????????????//?加鎖成功,?啟動(dòng)一個(gè)延時(shí)線程,?防止業(yè)務(wù)邏輯未執(zhí)行完畢就因鎖超時(shí)而使鎖釋放
????????????PostponeTask?postponeTask?=?new?PostponeTask(key,?value,?expireTime,?this);
????????????Thread?thread?=?new?Thread(postponeTask);
????????????thread.setDaemon(Boolean.TRUE);
????????????thread.start();
????????}
????????return?locked;
????}
????/**
?????*?解鎖
?????*?@param?key
?????*?@param?value
?????*?@return
?????*/
????public?Boolean?unLock(String?key,?String?value)?{
????????return?redisTemplate.execute((RedisCallback<Boolean>)?redisConnection?->?{
????????????Jedis?jedis?=?(Jedis)?redisConnection.getNativeConnection();
????????????Object?result?=?jedis.eval(RELEASE_LOCK_SCRIPT,?Collections.singletonList(key),?Collections.singletonList(value));
????????????if?(RELEASE_SUCCESS.equals(result))?{
????????????????return?Boolean.TRUE;
????????????}
????????????return?Boolean.FALSE;
????????});
????}
????/**
?????*?鎖延時(shí)
?????*?@param?key
?????*?@param?value
?????*?@param?expireTime
?????*?@return
?????*/
????public?Boolean?postpone(String?key,?String?value,?long?expireTime)?{
????????return?redisTemplate.execute((RedisCallback<Boolean>)?redisConnection?->?{
????????????Jedis?jedis?=?(Jedis)?redisConnection.getNativeConnection();
????????????Object?result?=?jedis.eval(POSTPONE_LOCK_SCRIPT,?Lists.newArrayList(key),?Lists.newArrayList(value,?String.valueOf(expireTime)));
????????????if?(POSTPONE_SUCCESS.equals(result))?{
????????????????return?Boolean.TRUE;
????????????}
????????????return?Boolean.FALSE;
????????});
????}
}
2)、PcInformationServiceImpl不需要改動(dòng)
3)、PostponeTask
package?com.cn.pinliang.common.thread;
import?com.cn.pinliang.common.util.DistributedLock;
public?class?PostponeTask?implements?Runnable?{
????private?String?key;
????private?String?value;
????private?long?expireTime;
????private?boolean?isRunning;
????private?DistributedLock?distributedLock;
????public?PostponeTask()?{
????}
????public?PostponeTask(String?key,?String?value,?long?expireTime,?DistributedLock?distributedLock)?{
????????this.key?=?key;
????????this.value?=?value;
????????this.expireTime?=?expireTime;
????????this.isRunning?=?Boolean.TRUE;
????????this.distributedLock?=?distributedLock;
????}
????@Override
????public?void?run()?{
????????long?waitTime?=?expireTime?*?1000?*?2?/?3;//?線程等待多長(zhǎng)時(shí)間后執(zhí)行
????????while?(isRunning)?{
????????????try?{
????????????????Thread.sleep(waitTime);
????????????????if?(distributedLock.postpone(key,?value,?expireTime))?{
????????????????????System.out.println("延時(shí)成功...........................................................");
????????????????}?else?{
????????????????????this.stop();
????????????????}
????????????}?catch?(Exception?e)?{
????????????????e.printStackTrace();
????????????}
????????}
????}
????private?void?stop()?{
????????this.isRunning?=?Boolean.FALSE;
????}
}




推薦閱讀:
【109期】面試官:我們說(shuō)StringBuilder是線程不安全的,是什么原因呢?
【108期】面試官:你真的知道 Java 類是如何被加載的嗎?
【107期】談?wù)劽嬖嚤貑?wèn)的Java內(nèi)存區(qū)域(運(yùn)行時(shí)數(shù)據(jù)區(qū)域)和內(nèi)存模型(JMM)
微信掃描二維碼,關(guān)注我的公眾號(hào)
朕已閱?
評(píng)論
圖片
表情

