public class RedisLockImplimplements RedisDistributionLock {
// 加锁超时时间,单位毫秒, 即:加锁时间内执行完操作,如果未完成会有并发现象
private static final long LOCK_TIMEOUT =5 *1000;
private static final LoggerLOG = LoggerFactory.getLogger(RedisLockImpl.class);
@Autowired
private StringRedisTemplateredisTemplate;
/**
* 加锁 取到锁加锁,取不到锁一直等待知道获得锁
*
* @param lockKey
* @param threadName
* @return
*/
@Override
public synchronized long lock(String lockKey, String threadName) {
LOG.info(threadName+"开始执行加锁");
while (true) {// 循环获取锁
// 锁时间
Long lock_timeout = currtTimeForRedis() +LOCK_TIMEOUT +1;
Boolean execute =redisTemplate.execute(new RedisCallback() {
@Override
public BooleandoInRedis(RedisConnection redisConnection)throws DataAccessException {
// 定义序列化方式
RedisSerializer serializer =redisTemplate.getStringSerializer();
byte[] value = serializer.serialize(lock_timeout.toString());
boolean flag = redisConnection.setNX(lockKey.getBytes(), value);
return flag;
}
});
if (execute) {
// 如果加锁成功
LOG.info(threadName +"加锁成功 ++++ 111111");
// 设置超时时间,释放内存
redisTemplate.expire(lockKey, LOCK_TIMEOUT *2, TimeUnit.MILLISECONDS);
return lock_timeout;
}else {
// 获取redis里面的时间
String result =redisTemplate.opsForValue().get(lockKey);
Long currt_lock_timeout_str = result ==null ?null : Long.parseLong(result);
long s = currt_lock_timeout_str - currtTimeForRedis();
LOG.info("currt_lock_timeout_str:"+currt_lock_timeout_str+"---"+s);
// 锁已经失效
if (currt_lock_timeout_str !=null && s <0) {
// 判断是否为空,不为空时,说明已经失效,如果被其他线程设置了值,则第二个条件判断无法执行
// 获取上一个锁到期时间,并设置现在的锁到期时间
Long old_lock_timeout_Str = Long
.valueOf(redisTemplate.opsForValue().getAndSet(lockKey, lock_timeout.toString()));
LOG.info("currt_lock_timeout_str:"+currt_lock_timeout_str+"old_lock_timeout_Str:"+old_lock_timeout_Str);
if (old_lock_timeout_Str !=null && old_lock_timeout_Str.equals(currt_lock_timeout_str)) {
// 多线程运行时,多个线程签好都到了这里,但只有一个线程的设置值和当前值相同,它才有权利获取锁
LOG.info(threadName +"加锁成功 ++++ 22222");
// 设置超时间,释放内存
redisTemplate.expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS);
// 返回加锁时间
return lock_timeout;
}
}
}
try {
LOG.info(threadName +"等待加锁, 睡眠200毫秒");
// TimeUnit.MILLISECONDS.sleep(100);
TimeUnit.MILLISECONDS.sleep(200);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 解锁
* 这里不需要synchronized,方法级是class对象加锁
* @param lockKey
* @param lockValue
* @param threadName
*/
@Override
public void unlock(String lockKey, long lockValue, String threadName) {
LOG.info(threadName +"执行解锁==========");//正常直接删除 如果异常关闭判断加锁会判断过期时间
// 获取redis中设置的时间
String result =redisTemplate.opsForValue().get(lockKey);
Long currt_lock_timeout_str = result ==null ?null : Long.valueOf(result);
// 如果是加锁者,则删除锁, 如果不是,则等待自动过期,重新竞争加锁
if (currt_lock_timeout_str !=null && currt_lock_timeout_str == lockValue) {
redisTemplate.delete(lockKey);
LOG.info(threadName +"解锁成功------------------");
}else {
LOG.info(threadName +"锁已被替换------------------");
}
}
/**
* 多服务器集群,使用下面的方法,代替System.currentTimeMillis(),获取redis时间,避免多服务的时间不一致问题!!!
*
* @return
*/
@Override
public long currtTimeForRedis() {
return redisTemplate.execute(new RedisCallback() {
@Override
public LongdoInRedis(RedisConnection redisConnection)throws DataAccessException {
return redisConnection.time();
}
});
}