注意:单个线程任务处理时间threadTime、获取锁的等待时间acquireTimeout、锁的过期时间timeout三者之间的关系:
1、threadTime < timeout (不然会出错)
2、acquireTimeout < threadTime < timeout (一般标准的设置时间方法)
废话不多说,直接上代码:
TestRedisLock类
public class TestRedisLock {
//redis分布式锁
public static void lock(){
String indentifier = null;
RedisLock redisLock = null;
try {
//toDO 增加分布式锁逻辑
redisLock = new RedisLock(RedisClient.jedisSentinelPool);
//首次获取到锁是必然的 acquireTimeout 为获取锁的等待时间,如果超过此时间就放弃 timeout 为锁的过期时间
indentifier = redisLock.lockWithTimeout("ss_test",500, 3000);
System.out.println(Thread.currentThread()+"尝试获取锁,indentifier:"+indentifier);
ThreadLocal<AtomicInteger> threadLocal = new ThreadLocal();
threadLocal.set(new AtomicInteger(0));
while (indentifier == null){//一直请求锁,直到拿到为止
threadLocal.get().getAndIncrement();
indentifier = redisLock.lockWithTimeout("ss_test",500, 3000);
//Thread.currentThread().sleep(100);
System.out.println(Thread.currentThread()+"尝试获取锁,indentifier:"+indentifier+",第"+threadLocal.get().get()+"次");
}
if(indentifier!=null){
//拿到锁开始干活
System.out.println(Thread.currentThread()+"获取到锁,indentifier:"+indentifier);
Thread.currentThread().sleep(2000);
//System.out.println(1/0);
//todo 执行任务
System.out.println(Thread.currentThread()+"执行完成");
}else {
System.out.println(Thread.currentThread()+"没有获取到锁");
}
} catch(Exception ex){
ex.printStackTrace();
} finally {
if(redisLock!=null && StringUtils.isNotEmpty(indentifier)){
redisLock.releaseLock("ss_test",indentifier);
System.out.println(Thread.currentThread()+"释放锁");
}
}
}
//todo 线程锁
public static void main(String[] args) {
//模拟100人抢购
for (int i=0;i<5;i++){
Thread thread = new Thread(){
@Override
public void run(){
lock();
}
};
thread.start();
}
}
}
RedisLock类
public class RedisLock {
private final JedisPool jedisPool;
private static final Logger logger = Logger.getLogger(RedisLock.class);
public RedisLock(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
/**
*
* @param locaName 锁的key
* @param acquireTimeout 尝试获取锁超时时间
* @param timeout key的超时时间
* @return 锁标识
*/
public String lockWithTimeout(String locaName,
long acquireTimeout, long timeout) {
Jedis conn = null;
String retIdentifier = null;
try {
// 获取连接
// 随机生成一个value
String identifier = UUID.randomUUID().toString(); //如果identifier存在则成功获取到锁,否则表明该锁已经被其他线程占用
// 锁名,即key值
String lockKey = "lock:" + locaName;
// 超时时间,上锁后超过此时间则自动释放锁
int lockExpire = (int)(timeout / 1000);
conn = jedisPool.getResource();
// 获取锁的超时时间,超过这个时间则放弃获取锁
long end = System.currentTimeMillis() + acquireTimeout;
while (System.currentTimeMillis() < end) {
//Jedis conn = jedisPool.getResource();
//System.out.println("等待时间:"+System.currentTimeMillis());
if (conn.setnx(lockKey, identifier) == 1) {
conn.expire(lockKey, lockExpire);
// 返回value值,用于释放锁时间确认
retIdentifier = identifier;
/*if (conn != null) {
conn.close();
//System.out.println("关闭连接");
}*/
return retIdentifier;
}
// 返回-1代表key没有设置超时时间,为key设置一个超时时间
if (conn.ttl(lockKey) == -1) {
conn.expire(lockKey, lockExpire);
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.error("RedisLock中断异常",e);
}
/* if (conn != null) {
conn.close();
//System.out.println("关闭连接");
}*/
}
} catch (JedisException e) {
logger.error("RedisLock获取锁异常",e);
//jedisPool.returnBrokenResource(conn);
} finally {
if (conn != null) {
conn.close();
// System.out.println("关闭连接");
}
}
return retIdentifier;
}
/**
* 释放锁
* @param lockName 锁的key
* @param identifier 释放锁的标识
* @return
*/
public boolean releaseLock(String lockName, String identifier) {
Jedis conn = null;
String lockKey = "lock:" + lockName;
boolean retFlag = false;
try {
conn = jedisPool.getResource();
while (true) {
// 监视lock,准备开始事务
conn.watch(lockKey);
// 通过前面返回的value值判断是不是该锁,若是该锁,则删除,释放锁
if (identifier.equals(conn.get(lockKey))) {
Transaction transaction = conn.multi();
transaction.del(lockKey);
List<Object> results = transaction.exec();
if (results == null) {
continue;
}
retFlag = true;
}
conn.unwatch();
break;
}
} catch (JedisException e) {
logger.error("RedisLock释放锁异常",e);
} finally {
if (conn != null) {
conn.close();
}
}
return retFlag;
}
}
本次测试模拟五个线程,acquireTimeout = 2500, threadTime = 2700 , timeout = 3000