/**
可以使用三种模式:
一次性模式,获取不到锁直接返回失败;
超时模式,每隔一小段时间重试获取锁,如果一直获取不到锁并且超时,就返回失败;
固定次数重试模式,每隔一小段时间重试获取锁,如果最后仍然获取不到锁,就返回失败。
使用了模板方法模式和适配器模式、回调风格。在成功获取锁之后、获取锁异常、获取锁失败、释放锁成功等各个点设计了回调。
*/
public abstract class RedisLockCallback<T> implements Callback<T>{
//private Logger logger = LoggerFactory.getLogger(RedisLockCallback.class);
private static final Logger logger = Logger.getLogger(RedisTest.class.getName());
private RedisLock redisLock;
private String lockKey;
/**锁的过期时间,毫秒*/
private long expireTime;
/**超时,毫秒*/
private long retryTimeout;
private String requestId = UUID.randomUUID().toString();
/**如果设置了超时,默认每20毫秒去获取一次锁。*/
private long sleepMillisecond = 20;
private int retryTimes;
private RetryMode retryMode;
private enum RetryMode{
DEFAULT,
RETRY_TIMEOUT,
RETRY_TIMES;
}
/**
*
* @param redisLock
* @param lockKey
* @param expireTime 锁的过期时间,单位秒
*/
public RedisLockCallback(RedisLock redisLock,String lockKey,long expireTime){
this.redisLock = redisLock;
this.lockKey = lockKey;
this.expireTime = expireTime;
this.retryMode = RetryMode.DEFAULT;
}
protected abstract T onGetLockSuccess();
protected void onReleaseLockSuccess(){
logger.info(String.format("释放redis锁成功,lockKey=%s,requestId=%s,expireTime=%s",lockKey, requestId, expireTime));
}
protected T onGetLockFailed(){
//logger.info("获取redis锁失败,lockKey={},requestId={},expireTime={}",lockKey, requestId, expireTime);
logger.info(String.format("获取redis锁失败,lockKey=%s,requestId=%s,expireTime=%s",lockKey, requestId, expireTime));
throw new RuntimeException(String.format("获取redis锁失败,lockKey=%s,requestId=%s,expireTime=%s", lockKey, requestId, expireTime));
}
protected void onReleaseLockFailed(){
//logger.error("释放redis锁失败,lockKey={},requestId={}",lockKey, requestId);
logger.info(String.format("释放redis锁失败,lockKey=%s,requestId=%s,expireTime=%s",lockKey, requestId, expireTime));
throw new RuntimeException(String.format("释放redis锁失败,lockKey=%s,requestId=%s", lockKey,requestId));
}
protected void onReleaseLockException(Exception e){
//logger.error("释放redis锁异常,lockKey={},requestId={}",lockKey, requestId,e);
logger.info(String.format("释放redis锁异常,lockKey=%s,requestId=%s",lockKey, requestId));
throw new RuntimeException(e);
}
protected RedisLockCallback<T> withRetryTimeout(long retryTimeout,long millisecond){
this.retryTimeout = retryTimeout;
Assert.isTrue(millisecond>=5,"millisecond必须大于等于5ms");
Assert.isTrue(retryTimeout>=5,"retryTimeout必须大于等于5ms");
this.sleepMillisecond = millisecond;
this.retryMode = RetryMode.RETRY_TIMEOUT;
return this;
}
/**
采用了链式调用风格,使调用更简洁方便
*/
protected RedisLockCallback<T> withRetryTimeout(long retryTimeout){
Assert.isTrue(retryTimeout>=5,"retryTimeout必须大于等于5ms");
this.retryTimeout = retryTimeout;
this.retryMode = RetryMode.RETRY_TIMEOUT;
return this;
}
protected RedisLockCallback<T> withRetryTimes(int retryTimes,long millisecond){
Assert.isTrue(millisecond>=5,"millisecond必须大于等于5ms");
Assert.isTrue(retryTimes>=0,"retryTimes必须大于等于0");
this.retryTimes = retryTimes;
this.sleepMillisecond = millisecond;
this.retryMode = RetryMode.RETRY_TIMES;
return this;
}
protected RedisLockCallback<T> withRetryTimes(int retryTimes){
Assert.isTrue(retryTimes>=0,"retryTimes必须大于等于0");
this.retryTimes = retryTimes;
this.retryMode = RetryMode.RETRY_TIMES;
return this;
}
@Override
public T execute() throws InterruptedException{
//boolean locked = redisLock.setValueNxExpire(lockKey,requestId, ""+expireTime);
boolean locked = false;
switch(retryMode){
case DEFAULT:
locked = redisLock.tryGetDistributedLock(lockKey, requestId, expireTime*1000);
break;
case RETRY_TIMEOUT:
int i = retryTimes;
while(true){
locked = redisLock.tryGetDistributedLock(lockKey, requestId, expireTime*1000);
if(locked){
break;
}
i--;
if(i>=0){
Thread.sleep(sleepMillisecond);
}
}
break;
case RETRY_TIMES:
long millisecond = System.currentTimeMillis();
while(true){
locked = redisLock.tryGetDistributedLock(lockKey, requestId, expireTime*1000);
if(locked){
break;
}
if(retryTimeout+millisecond>System.currentTimeMillis()){
Thread.sleep(sleepMillisecond);
}else{
break;
}
}
break;
default:
throw new RuntimeException("未知的retryMode值:"+retryMode);
}
if(locked){
try{
return onGetLockSuccess();
}finally{
Boolean unlockSuccess = null;
try{
//boolean unlockSuccess = redisLock.delValue(lockKey)>0;
logger.info("正在释放锁...");
unlockSuccess = redisLock.releaseDistributedLock(lockKey, requestId);
}catch(Exception e){
onReleaseLockException(e);
}
if(unlockSuccess!=null){
if(unlockSuccess){
onReleaseLockSuccess();
}else{
onReleaseLockFailed();
}
}
}
}else{
return onGetLockFailed();
}
}
}
public class RedisTest {
private static final Logger logger = Logger.getLogger(RedisTest.class.getName());
static ExecutorService executor = Executors.newFixedThreadPool(30);
@Test
public void test() throws InterruptedException, IOException {
String lockKey = "aaaa";
long expireTime = 1200;
long timeout = 1000 * 600;
logger.info("begin...");
for (int i = 0; i < 5; i++) {
executor.submit(() -> {
//注意,这个lambda里面的异常,如果不捕获,将会被吞噬,异常日志都没有!!!
try{
Jedis redis = new Jedis("localhost", 6379);
// redis.auth("");
RedisLock redisLock = new RedisLock();
redisLock.setRedis(redis);
RedisLockCallback<Object> cb = new RedisLockCallback<Object>(redisLock,lockKey,expireTime) {
@Override
protected Object onGetLockSuccess() {
try {
logger.info("onGetLockSuccess");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
logger.info("InterruptedException in onGetLockSuccess");
throw new RuntimeException(e);
}
return null;
}
@Override
protected void onReleaseLockSuccess(){
redis.close();
}
};
cb
.withRetryTimeout(timeout,20)
.withRetryTimes(1)
.execute();
}catch(Exception e){
e.printStackTrace();
}
});
}
executor.shutdown();
while (true) {
boolean finished = executor.awaitTermination(30, TimeUnit.SECONDS);
if (finished) {
break;
}
}
}
}
public class RedisLock {
// private static Logger logger = LoggerFactory.getLogger(RedisLock.class);
private static final String LOCK_SUCCESS = "OK";
private static final Long RELEASE_SUCCESS = 1L;
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
private Jedis redis;
public void setRedis(Jedis redis) {
this.redis = redis;
}
/**
* 尝试获取分布式锁
*
* @param lockKey
* 锁
* @param requestId
* 请求标识
* @param expireTime 单位为毫秒
* 超期时间
* @return 是否获取成功
*/
public boolean tryGetDistributedLock(String lockKey, String requestId, long expireTime) {
String result = redis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
/**
*
* 释放分布式锁
*
* @param jedis
* Redis客户端
* @param lockKey
* 锁
* @param requestId
* 请求标识,防止释放别人申请的锁。
* @return 是否释放成功
*/
public boolean releaseDistributedLock(String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = redis.eval(script, Collections.singletonList(lockKey),
Collections.singletonList(requestId));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
}
public interface Callback<T> {
T execute() throws InterruptedException;
}
reids分布式锁实现
猜你喜欢
转载自blog.csdn.net/zhoujiaping123/article/details/80252274
今日推荐
周排行