在我的上一篇文章中,关于redis分布式锁的写法,释放锁还有些缺陷,细节见评论部分,本文进一步做了完善。
分布式锁实现方案2、基于Redis的SET操作实现的分布式锁
package com.alioo.common.lock;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* 基于Redis的SET操作实现的分布式锁
* </pre>
*
* @author lzc.java@icloud.com
*/
@Slf4j
public class RedisDistributedLock {
private JedisFacade redis;
// 锁key
private String lockKey;
// 锁value
private String lockVal;
// 是否获得锁
private boolean locked;
private static final String RELEASE_LOCK_LUA_SCRIPT
= "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
/**
* @param redis
* @param lockKey lockKey
*/
public RedisDistributedLock(JedisFacade redis, String lockKey) {
this.redis = redis;
this.lockKey = lockKey;
}
/**
* 阻塞式获取锁 ,不过有超时时间waitTime,超过了waitTime还未获取到锁将直接返回false
*
* @param lockTime 获得锁之后过期时间
* @param waitTime 获得锁最多等待时间
* @param unit
* @return
*/
protected boolean lock(long lockTime, long waitTime, TimeUnit unit) {
try {
// 超时控制 的时间可以从本地获取, 因为这个和锁超时没有关系, 只是一段时间区间的控制
long start = System.currentTimeMillis();
long milliTimeout = unit.toMillis(waitTime);
while (System.currentTimeMillis() - start < milliTimeout) {
boolean flag = tryLock(lockTime, unit);
if (flag) {
return true;
}
Thread.sleep(10L);
}
} catch (InterruptedException e) {
return false;
}
return false;
}
/**
* 非阻塞,立即返回是否获取到锁
*
* @param lockTime 获得锁之后过期时间
* @param unit
* @return
*/
public boolean tryLock(long lockTime, TimeUnit unit) {
long milliLockTime = unit.toMillis(lockTime);
//锁过期时间
long lockExpireTime = System.currentTimeMillis() + milliLockTime;
lockVal = String.valueOf(lockExpireTime);
String ret = redis.set(lockKey, lockVal, "NX", "PX", milliLockTime);
if ("OK".equals(ret)) {
// 获取到锁, 设置相关标识
locked = true;
return true;
}
return false;
}
public boolean isLocked() {
return locked;
}
/**
* 释放锁
*/
public void unlock() {
if (!locked) {
return;
}
// 当前线程加的锁也许已经过期自动释放了,然后又被其它线程加锁了,所以在释放锁的时候进一步根据lockVal再check下
Object ret = redis.eval(RELEASE_LOCK_LUA_SCRIPT, Collections.singletonList(lockKey),
Collections.singletonList(lockVal));
log.info("释放锁lockKey:{},lockVal:{},ret:{}", lockKey, lockVal, ret);
}
}
使用示例
String lockKey = String.format("%s:%s:%s:%s", REDIS_LOCK_PREFIX, "biz_type", "sub_sub_type", userId);
RedisDistributedLock lock = new RedisDistributedLock(jedisFacade, lockKey);
Boolean result;
try {
boolean locked = lock.tryLock(5000, TimeUnit.MILLISECONDS);
if (!locked) {
throw new ServiceException(SysErrorCode.REDIS_LOCK_ERROR);
}
// do your service
doYourService()
} finally {
lock.unlock();
}