20220506_Redisson分布式锁hash中key的创建分析
1概述
1.1时序分析
// 将哈希表 key 中的域 field 的值设为 value
hset key field value
1.1.1.Spring 自动装配Redisson
Redisson对象依赖connectionManager,当构建Redisson对象,通过配置创建 connectionManager
connectionManager里面有个id属性,由uuid生成。
1.1.2.根据key名称,获取某个RedissonLock
RLock rLock = redissonClient.getLock(LOCK_GOODS_ADD_PREFIX + goodsId);
image-20220506192111362.png
1.1.3.线程A来了, 开始加锁tryLock,即向Redis里面写入缓存数据和缓存失效时间,数据类型为:hash。
boolean lockResult = rLock.tryLock(100, 30, TimeUnit.SECONDS);
// 加锁的Lua脚本
// keys:hash的key-->KEY[1]
// args:
// key的过期时间 -->ARGV[1]、
// field(这里值redisson固定为1)-->ARGV[2](组成部分:当前RedissonLock的uuid:当前线程的值)
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
"if (redis.call('exists', KEYS[1]) == 0) then " +
"redis.call('hset', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"return redis.call('pttl', KEYS[1]);",
Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
hash的key为:goods:add:00001
field为:当前RedissonLock的uuid:当前线程的值,即1ea0b24e-e1f6-4a1b-88fc-f31f2a909bdc:51
value为:默认为1(支持可重入,累加)
image-20220506192041800.png1.1.4.线程B来了, 开始加锁tryLock,获得锁成功以后,,即向Redis里面写入缓存数据和缓存失效时间,数据类型为:hash。
线程A如果已经释放锁,则线程加锁成功,否则等待指定的时间。
boolean lockResult = rLock.tryLock(100, 30, TimeUnit.SECONDS);
hash的key为:goods:add:00001
field为:当前RedissonLock的uuid:当前线程的值,即1ea0b24e-e1f6-4a1b-88fc-f31f2a909bdc:52
value为:默认为1(支持可重入,累加)
image-20220506192058810.png2代码示例
2.1testTryLock
/**
* testTryLock
*/
private void testTryLock(String goodsId, String strThreadInfo) {
// RLock关联某商品的锁
RLock rLock = redissonClient.getLock(LOCK_GOODS_ADD_PREFIX + goodsId);
try {
log.info("当前线程[{}:{}]开始获取锁", Thread.currentThread().getName(), Thread.currentThread().getId());
// 1.获取锁最多等待 100 s
// wai30s后 key过期
// key:b2b62744-3392-48ca-83a3-eff8ef638d55: 50
// value:1
boolean lockResult = rLock.tryLock(100, 30, TimeUnit.SECONDS);
System.out.println("getName()"+rLock.getName());
if (!lockResult) {
log.info("当前线程[{}:{}]获取锁失败", Thread.currentThread().getName(), Thread.currentThread().getId());
return;
}
log.info("当前线程[{}:{}]获取锁成功", Thread.currentThread().getName(), Thread.currentThread().getId());
// 2.业务操作
// 模拟较长时间 3<100,rLock.isHeldByCurrentThread()==>false
// TimeUnit.SECONDS.sleep(100);
// 模拟较长耗时时间,10s
// rLock.isHeldByCurrentThread()==>true
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException ex) {
log.info("当前线程:[{}:{}]获取锁异常,异常原因:{}!",
Thread.currentThread().getName(),
Thread.currentThread().getId(),
ex);
} finally {
log.info("当前线程[{}:{}]准备释放锁", Thread.currentThread().getName(), Thread.currentThread().getId());
// 释放锁的时候最好判断当前线程是否持有锁,否则会抛出异常
if (rLock.isHeldByCurrentThread()) {
log.info("当前线程[{}:{}]释放锁成功", Thread.currentThread().getName(), Thread.currentThread().getId());
rLock.unlock();
} else {
// 如锁可能已经过期,别的线程占有了,字段中key对应的value:为其他线程
log.info("锁非当前线程持有,不需要释放[{}:{}]", Thread.currentThread().getName(), Thread.currentThread().getId());
}
}
}
网友评论