直接看代码
package com.jd.medicine.base.common.spring.util;
import com.jd.jim.cli.Cluster;
import com.jd.medicine.base.common.logging.LogUtil;
import com.jd.medicine.base.common.util.StringUtil;
import org.slf4j.Logger;
import java.util.concurrent.TimeUnit;
/**
* Redis实现的简单分布式锁
* <p></p>
* Created by tanwei3 on 2016/08/02.
*/
public class RedisLock {
private static final Logger LOG = LogUtil.getLogger(RedisLock.class);
private Cluster jimClient;
/**
* 非原子操作
* 尝试获取分布式锁,最多重试retryTimes次
*
* @param lockKey 锁缓存key
* @param value 锁缓存key
* @param expireTime 锁缓存有效期
* @param retryTimes 失败重试次数
* @param retrySleepMills 失败重试单隔毫秒
* @return boolean true:成功 false:失败
*/
@Deprecated
public boolean tryLockWithRetry(String lockKey, String value, long expireTime, int retryTimes, long retrySleepMills) {
if(StringUtil.isBlank(lockKey) || StringUtil.isBlank(value) || expireTime <= 0 || retryTimes <= 0 || retrySleepMills <= 0){
LOG.error("RedisLockImpl-->tryLockWithRetry| invalid parameter");
return false;
}
while(retryTimes > 0){
if(tryLock(lockKey, value, expireTime)){
LOG.debug("RedisLockImpl-->tryLockWithRetry get lock success| retryTimes:{}", retryTimes);
return true;
}else{
try {
TimeUnit.MILLISECONDS.sleep(retrySleepMills);
} catch (InterruptedException e) {
LOG.error("RedisLockImpl-->tryLockWithRetry thread InterruptedException");
}
}
retryTimes--;
}
//检测一下ttl是否是-1,表明key未设置过期时间
try {
Long ttlVal = jimClient.ttl(lockKey);
if(ttlVal !=null && ttlVal == -1){
jimClient.expire(lockKey, expireTime, TimeUnit.SECONDS);
}
} catch (Exception e) {
LOG.error("RedisLockImpl-->tryLockWithRetry reset expire execute error! | lockKey={}", lockKey, e);
}
return false;
}
/**
* 尝试获取分布式锁,最多重试retryTimes次
* @param lockKey 锁缓存key
* @param requestId 同一应用内的唯一ID,
* 使用com.jd.medicine.base.common.logging.rxlog.UniqRequestIdGen.generateReqId()方法生成
* 可以传任意非空和空串值
* 但【强烈建议】使用requestId,以便以后自己的锁只有自己能解
* @param expireTime 锁缓存有效期
* @param timeUnit 锁缓存有效期时间单位
* @param retryTimes 失败重试次数
* @param retrySleepMills 失败重试单隔【毫秒】
* @return
*/
public boolean tryLockWithRetry(String lockKey, String requestId, long expireTime, TimeUnit timeUnit, int retryTimes, long retrySleepMills) {
if(StringUtil.isBlank(lockKey) || StringUtil.isBlank(requestId) || expireTime <= 0 || timeUnit == null
|| retryTimes <= 0 || retrySleepMills <= 0){
LOG.error("RedisLockImpl --> tryLockWithRetry | invalid parameter");
return false;
}
while(retryTimes > 0){
if(tryLock(lockKey, requestId, expireTime, timeUnit)){
LOG.debug("RedisLockImpl --> tryLockWithRetry get lock success | retryTimes:{}", retryTimes);
return true;
}else{
try {
TimeUnit.MILLISECONDS.sleep(retrySleepMills);
} catch (InterruptedException e) {
LOG.error("RedisLockImpl --> tryLockWithRetry thread InterruptedException");
}
}
retryTimes--;
}
return false;
}
/**
* 尝试获取分布式锁,最多重试retryTimes次 redis指令执行异常会抛出
* @param lockKey 锁缓存key
* @param requestId 同一应用内的唯一ID,
* 使用com.jd.medicine.base.common.logging.rxlog.UniqRequestIdGen.generateReqId()方法生成
* 可以传任意非空和空串值
* 但【强烈建议】使用requestId,以便以后自己的锁只有自己能解
* @param expireTime 锁缓存有效期
* @param timeUnit 锁缓存有效期时间单位
* @param retryTimes 失败重试次数
* @param retrySleepMills 失败重试单隔【毫秒】
* @return
*/
public boolean tryLockWithRetryWithThrow(String lockKey, String requestId, long expireTime, TimeUnit timeUnit, int retryTimes, long retrySleepMills) {
if(StringUtil.isBlank(lockKey) || StringUtil.isBlank(requestId) || expireTime <= 0 || timeUnit == null
|| retryTimes <= 0 || retrySleepMills <= 0){
LOG.error("RedisLock->tryLockWithRetryWithThrow|invalid parameter");
return false;
}
while(retryTimes > 0){
if(tryLockWithThrow(lockKey, requestId, expireTime, timeUnit)){
LOG.debug("RedisLock->tryLockWithRetryWithThrow|get lock success|retryTimes:{}", retryTimes);
return true;
}else{
try {
TimeUnit.MILLISECONDS.sleep(retrySleepMills);
} catch (InterruptedException e) {
LOG.error("RedisLock->tryLockWithRetryWithThrow|thread InterruptedException");
}
}
retryTimes--;
}
return false;
}
/**
* 非原子操作
* 尝试获取锁 如果锁可用 立即返回true, 否则返回false
* 禁止使用永久锁,因为程序崩溃或者断电时有可能无法执行到finally,也就无法释放锁了
* @param lockKey 锁缓存key
* @param value 锁缓存key
* @param expireTime 锁缓存有效期
* @return 是否获得锁
*/
@Deprecated
public boolean tryLock(String lockKey, String value, long expireTime) {
if(StringUtil.isBlank(lockKey) || StringUtil.isBlank(value) || expireTime <= 0){
LOG.error("RedisLockImpl-->tryLock | invalid parameter");
return false;
}
try {
LOG.trace("RedisLockImpl-->tryLock | lockKey:{}, value:{}, expireTime:{}",lockKey,value,expireTime);
boolean successFlag = jimClient.setNX(lockKey, value);
if (successFlag) {
boolean expireFlag = jimClient.expire(lockKey, expireTime, TimeUnit.SECONDS);
LOG.debug("RedisLockImpl-->tryLock set expire| expireFlag:{}, lockKey:{}, value:{}, expireTime:{}", expireFlag,lockKey,value,expireTime);
return true;
} else {
// 存在锁,此时有可能前一个节点已经释放锁,或者其他节点获取了锁
if(LOG.isDebugEnabled()){
//里面有jimClient.get操作,所以加一下判断
LOG.debug("RedisLockImpl-->tryLock lock conflict| lockKey:{}, value:{}",lockKey,jimClient.get(lockKey));
}
return false;
}
} catch (RuntimeException e) {
LOG.error("RedisLockImpl-->tryLock execute error", e);
}
return false;
}
/**
* 尝试获取锁 如果锁可用 立即返回true, 否则返回false
* @param lockKey
* @param requestId 同一应用内的唯一ID,
* 使用com.jd.medicine.base.common.logging.rxlog.UniqRequestIdGen.generateReqId()方法生成
* 可以传任意非空和空串值
* 但【强烈建议】使用requestId,以便以后自己的锁只有自己能解
* @param expireTime 超时时间
* @param timeUnit 超时时间单位
* @return
*/
public boolean tryLock(String lockKey, String requestId, long expireTime, TimeUnit timeUnit) {
if(StringUtil.isBlank(lockKey) || StringUtil.isBlank(requestId) || expireTime <= 0 || timeUnit == null){
LOG.error("RedisLockImpl --> tryLock | invalid parameter");
return false;
}
try {
LOG.trace("RedisLockImpl --> tryLock | lockKey:{}, value:{}, expireTime:{}, timeUnit:{}",
lockKey, requestId, expireTime, timeUnit);
// exist:false 不存在lockKey时才能操作 lockKey
return jimClient.set(lockKey, requestId, expireTime, timeUnit, false);
} catch (Exception e) {
LOG.error("RedisLockImpl --> tryLock execute error", e);
}
return false;
}
/**
* 尝试获取锁 如果锁可用 立即返回true,如果执行redis指令异常则抛出,否则返回false
* @param lockKey
* @param requestId 同一应用内的唯一ID,
* 使用com.jd.medicine.base.common.logging.rxlog.UniqRequestIdGen.generateReqId()方法生成
* 可以传任意非空和空串值
* 但【强烈建议】使用requestId,以便以后自己的锁只有自己能解
* @param expireTime 超时时间
* @param timeUnit 超时时间单位
* @return
*/
public boolean tryLockWithThrow(String lockKey, String requestId, long expireTime, TimeUnit timeUnit) {
if(StringUtil.isBlank(lockKey) || StringUtil.isBlank(requestId) || expireTime <= 0 || timeUnit == null){
LOG.error("RedisLock->tryLock|invalid parameter");
return false;
}
try {
LOG.trace("RedisLock->tryLock|lockKey:{}, value:{}, expireTime:{}, timeUnit:{}",
lockKey, requestId, expireTime, timeUnit);
// exist:false 不存在lockKey时才能操作 lockKey
return jimClient.set(lockKey, requestId, expireTime, timeUnit, false);
} catch (Exception e) {
LOG.error("RedisLock->tryLock|execute error", e);
throw e;
}
}
/**
* 释放锁
* @param lockKey
*/
public void unLock(String lockKey) {
if(StringUtil.isBlank(lockKey)){
LOG.error("RedisLockImpl-->unLock| invalid parameter");
return;
}
try {
jimClient.del(lockKey);
if(!jimClient.exists(lockKey)) {
//删除成功
LOG.debug("RedisLockImpl-->unLock release lock success| lockKey:{}", lockKey);
} else {
LOG.error("RedisLockImpl-->unLock release lock error| lockKey:{}", lockKey);
}
} catch (RuntimeException e) {
LOG.error("RedisLockImpl-->unLock execute error ", e);
}
}
public void setJimClient(Cluster jimClient) {
this.jimClient = jimClient;
}
}