简介
项目中采用了多tomcat节点部署实现负载均衡,针对抢单、秒杀等场景需要保证数据有序性(业务需要),则要使用分布式锁。
这个是借鉴了另外一个jedis实现(https://github.com/abelaska/jedis-lock),这里改成了redisTeamplate实现,直接和spring集成。
源码
package com.andmobi.outservice.redis.lock;
import java.util.UUID;
import org.springframework.data.redis.core.RedisTemplate;
/**
* 分布式锁<br>
* 基于redis,RedisTemplate 实现<br>
* @author gin
*
*/
public class DistributionLock {
private static final Lock NO_LOCK = new Lock(new UUID(0l,0l), 0l);
private static final int ONE_SECOND = 1000;
public static final int DEFAULT_EXPIRY_TIME_MILLIS = 60 * ONE_SECOND;
public static final int DEFAULT_ACQUIRE_TIMEOUT_MILLIS = 10 * ONE_SECOND;
public static final int DEFAULT_ACQUIRY_RESOLUTION_MILLIS = 100;
private RedisTemplate redisTp;
private final String lockKeyPath;
private final int lockExpiryInMillis;
private final int acquiryTimeoutInMillis;
private final UUID lockUUID;
private Lock lock = null;
/**
* 获取分布式锁
* @param redisTp redisTeamplate
* @return true为获取成功,false为获取失败
* @throws InterruptedException
*/
protected synchronized boolean acquire(RedisTemplate redisTp) throws InterruptedException {
//从常量中获取设置的超时时间
int timeout = acquiryTimeoutInMillis;
while (timeout >= 0) {
try{
//每次先创建一个新锁,定义超时时间点
final Lock newLock = asLock(System.currentTimeMillis() + lockExpiryInMillis);
//调用setnx 如果redis返回执行成功,则认为当前该key不存在,意味着当前线程获取到了redis上lockKeyPath对应的锁
//调用完 ,redis上 key[lockKeyPath]= newLock的信息
if (redisTp.opsForValue().setIfAbsent(lockKeyPath, newLock.toString())) {
this.lock = newLock;
//告诉业务代码,已成功获取到锁,可以执行锁内代码
return true;
}
//如果没获取到锁,则将该锁对应的值(即其他线程存入的newLock信息) 取出来
final String currentValueStr =String.valueOf( redisTp.opsForValue().get(lockKeyPath) );
//反序列化为锁对象
final Lock currentLock = Lock.fromString(currentValueStr);
//如果该锁已过期,或者本身是自己所创建
if (currentLock.isExpiredOrMine(lockUUID)) {
//将新锁替换上去并获取此刻的旧值
String oldValueStr = String.valueOf(redisTp.opsForValue().getAndSet(lockKeyPath, newLock.toStri