一: 分布式锁要求
- 在任意时刻只能有一个线程可持有该锁。
- 该锁只能被加锁人解锁。
- 不会发生死锁。
二: 分布式锁实现
2.1 基于Jedis 实现分布式锁
2.1.1 引入依赖
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
2.1.2 加锁的代码
- Jedis 提供的方法
/**
*
* @param jedis jedis客户端
* @param key 锁的key
* @param value 客户端标识
* @param nxxx NX 或者 XX 。 NX Only set the key if it does not already exist. XX Only set the key if it already exist
* @param expx EX 或者 PX。 xpire time units: EX = seconds; PX = milliseconds
* @param time expire time in the units of expx
* @return
*/
public boolean lockRedis(Jedis jedis,String key, String value, String nxxx, String expx, int time){
String status = jedis.set(key,value,nxxx,expx,time);
if("OK".equals(status)){
return true;
}else{
return false;
}
}
ps: 这里主要说一下 nxxx 的作用 NX 只有 当 key不存在的时候进行 set操作。xx 只有当key存在的时候进行set操作。 所以显而易见 我们固定传 NX
- lua script 进行加锁
/**
*
* @param jedis jedis客户端
* @param key 锁的key
* @param value 客户端标识
* @param time 超时时间
* @return
*/
public boolean lockRedis(Jedis jedis,String key, String value, int time){
String luaScript = ""
+ "\nlocal respStr = tonumber(redis.call('SETNX', KEYS[1],ARGV[1]));"
+ "\nredis.call('PEXPIRE',KEYS[1],ARGV[2]);"
+ "\nreturn respStr";
List<String> keys = new ArrayList<String>();
keys.add(key);
List<String> args = new ArrayList<String>();
args.add(value);
args.add(time+"");
Long ret = (Long) jedis.eval(luaScript, keys, args);
if( new Long(1).equals(ret)){
return true;
}
return false;
}
2.1.3 解锁代码
/**
*
* @param jedis jedis客户端
* @param key 锁的key
* @param value 客户端标识
* @return
*/
public boolean unlockRedis(Jedis jedis,String key, String value){
String luaScript=""
+"\nlocal v = redis.call('GET', KEYS[1]);"
+"\nlocal r = 0;"
+"\nif v == ARGV[1] then"
+"\nr = tonumber(redis.call('DEL',KEYS[1]));"
+"\nend"
+"\nreturn r";
List<String> keys = new ArrayList<String>();
keys.add(key);
List<String> args = new ArrayList<String>();
args.add(value);
Long r=(Long) jedis.eval(luaScript, keys, args);
if( new Long(1).equals(r)){
return true;
}
return false;
}
ps: redis 会在 lua 命令执行结束之后再执行其他命令。