import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
/**
* @author leitao.
* @time: 2018/3/26 9:17
* @version: 1.0
* @description: 基于codis实现分布式锁
**/
@Component
public class DistributedLock {
private Jedis jedis;
/**
* 分布式锁key前缀
*/
private final static String LOCK_KEY_PREFIX = "DL_";
/**
* 锁过期时间100毫秒
*/
public final static Long TIMEOUT_100MS = 100L;
public final static Long TIMEOUT_2000MS = 2000L;
/**
* 等待时间,30毫秒之后再重新获取锁
*/
private final static Long WAIT_TIME = 30L;
/**
* 尝试获取锁次数
*/
private final static int TRY_TIMES = 3;
private static final Log LOG = LogFactory.getLog(DistributedLock.class);
/**
* 1. setnx(lockkey, 当前时间+过期超时时间) ,如果返回1,则获取锁成功;如果返回0则没有获取到锁,转向2.
* 2. get(lockkey)获取值oldExpireTime ,并将这个value值与当前的系统时间进行比较,如果小于当前系统时间,
* 则认为这个锁已经超时,可以允许别的请求重新获取,转向3.
* 3. 计算newExpireTime=当前时间+过期超时时间,然后getset(lockkey, newExpireTime) 会返回当前lockkey的值currentExpireTime.
* 4. 判断currentExpireTime与oldExpireTime 是否相等,如果相等,说明当前getset设置成功,获取到了锁,如果不相等,
* 说明这个锁又被别的请求获取走了,那么当前请求可以直接返回失败.
*
* @param key
* @return
*/
public boolean lock(String key, Long timeout) {
Long setnx = jedis.setnx(this.getDistributedLockKey(key), this.getExpireTime(timeout));
if (1L == setnx) {
//获取锁成功
return true;
}
String oldExpireTimeStr = jedis.get(this.getDistributedLockKey(key));
if (NumberUtils.isNumber(oldExpireTimeStr)) {
Long oldExpireTime = Long.parseLong(oldExpireTimeStr);
if (oldExpireTime < System.currentTimeMillis()) {
String currentExpireTimeStr = jedis.getSet(this.getDistributedLockKey(key), this.getExpireTime(timeout));
if (StringUtils.equals(currentExpireTimeStr, oldExpireTimeStr)) {
return true;
}
}
}
return false;
}
/**
* 若第一次没有获取到锁,则尝试直到获取到锁为止,做多尝试次数3次,间隔30ms尝试一次
*
* @param key
* @return
*/
public boolean tryLock(String key, Long timeout) {
for (int i = 0; i < TRY_TIMES; i++) {
Long setnx = jedis.setnx(this.getDistributedLockKey(key), this.getExpireTime(timeout));
if (1L == setnx) {
//获取锁成功
return true;
}
String oldExpireTimeStr = jedis.get(this.getDistributedLockKey(key));
if (NumberUtils.isNumber(oldExpireTimeStr)) {
Long oldExpireTime = Long.parseLong(oldExpireTimeStr);
if (oldExpireTime < System.currentTimeMillis()) {
String currentExpireTimeStr = jedis.getSet(this.getDistributedLockKey(key), this.getExpireTime(timeout));
if (StringUtils.equals(currentExpireTimeStr, oldExpireTimeStr)) {
return true;
}
}
}
try {
Thread.sleep(WAIT_TIME);
} catch (InterruptedException e) {
LOG.error(e.getMessage(), e);
}
}
return false;
}
/**
* 释放锁
*
* @param key
*/
public void unLock(String key) {
jedis.del(this.getDistributedLockKey(key));
}
/**
* 获取分布式锁的key
*
* @param key
* @return
*/
private String getDistributedLockKey(String key) {
return LOCK_KEY_PREFIX + key;
}
/**
* 获取分布式锁过期时间
*
* @return
*/
private String getExpireTime(Long timeout) {
return System.currentTimeMillis() + timeout + "";
}
}