Redis实现的简单分布式锁

直接看代码

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实现的简单分布式锁
 * 

* 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; } }

 

你可能感兴趣的:(Java并发编程及并发面试点)