redis 不可重入、非公平锁的实现
加锁使用 set 操作
解锁使用lua脚本
local key= KEYS[1]
local lockId=ARGV[1]
local result=0
local val= redis.call('get',key);
if(val==nil) then
result=0
elseif (val==lockId) then
redis.call('del',key)
result=1
else
result=2
end
return result
锁的实现:
package xxxxxxxxxxxxxxxxxxxxx.lock;
import com.google.common.base.Throwables;
import xxxxxxxxxx.R2mClusterClient;
import lombok.extern.slf4j.Slf4j;
import java.util.UUID;
/**
* r2m 锁实现
* Created by lxn on 2019/4/11.
*/
@Slf4j
public class R2mLock {
/**
* 释放锁的lua脚本
*/
private static String relaseScript="local key= KEYS[1]\n" +
"local lockId=ARGV[1]\n" +
"\n" +
"local result=0\n" +
"local val= redis.call('get',key);\n" +
"\n" +
"if(val==nil) then\n" +
" result=0\n" +
"elseif (val==lockId) then\n" +
" redis.call('del',key)\n" +
" result=1\n" +
"else\n" +
" result=2\n" +
"end\n" +
"return result";
/**
* 最长锁时间
*/
private long defaultLockTime =10000;
/**
* 锁的key
*/
private String lockKey;
/**
* r2m客户端
*/
private R2mClusterClient r2mClusterClient;
private String lockId;
public R2mLock(String lockKey, R2mClusterClient r2mClusterClient) {
this.lockKey = lockKey;
this.r2mClusterClient = r2mClusterClient;
}
/**
* 获取锁
* @param waitTime 毫秒
* @throws LockTimeOutException
*/
public void tryLock(long waitTime) throws LockTimeOutException {
lockId= UUID.randomUUID().toString();
long start=System.currentTimeMillis();
while(System.currentTimeMillis()-start<waitTime) {
String result = r2mClusterClient.set(lockKey, lockId, "NX", "PX", defaultLockTime);
if ("OK".equals(result)){//获取到锁
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
throw new LockTimeOutException();
}
/**
* 释放锁
* @throws LockReleaseException
*/
public void releaseLock() {
if (lockId==null){
return;
}
try {
Object result=r2mClusterClient.eval(relaseScript,lockKey,lockId);
if ("0".equals(result.toString())){
log.error("锁不存在,无法释放");
}
if ("2".equals(result.toString())){
log.error("非当前锁,无法释放");
}
}catch (Exception e){
log.error("解锁失败{}", Throwables.getStackTraceAsString(e));
}
}
}
锁的使用示例:
使用时记得要双重检查!
R2mLock lock=new R2mLock(key,r2mClusterClient);
try {
lock.tryLock(3000);
//业务代码
} catch (LockTimeOutException e) {
throw new OrderTradeException(ErrorCodeEnum.GET_LOCK_ERROR);
}finally {
lock.releaseLock();
}