<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.8.2</version> </dependency>
@Test public void tt() { Config config = new Config(); config.useSingleServer().setAddress("127.0.0.1:6399").setDatabase(0); // 构造RedissonClient RedissonClient redissonClient = Redisson.create(config); // 设置锁定资源名称 RLock disLock = redissonClient.getLock("helloRedissionLock"); boolean isLock; try { //尝试获取分布式锁 isLock = disLock.tryLock(500, 15000, TimeUnit.MILLISECONDS); if (isLock) { //TODO if get lock success, do something; Thread.sleep(15000); } } catch (Exception e) { } finally { // 无论如何, 最后都要解锁 disLock.unlock(); } }
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));
解读:这是一段lua的脚本
第一个if:判断key = helloRedissionLock是否存在?若不存(==0)在则通过hset命令设置 key = helloRedissionLock的hash对象(key=当前线程ID:1 value=1),并且设置过期时间
第二个if:判断key = helloRedissionLock和hash对象(key=当前线程ID:1 value=1)?若存在(==1)则:1.设置hash对象的value+1,2.重新设置过期时间
return:key = helloRedissionLock的过期时间
备注:Hincrby 命令用于为哈希表中的字段值加上指定增量值,该点是reentrant lock的关键
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, "if (redis.call('exists', KEYS[1]) == 0) then " + "redis.call('publish', KEYS[2], ARGV[1]); " + "return 1; " + "end;" + "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " + "return nil;" + "end; " + "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " + "if (counter > 0) then " + "redis.call('pexpire', KEYS[1], ARGV[2]); " + "return 0; " + "else " + "redis.call('del', KEYS[1]); " + "redis.call('publish', KEYS[2], ARGV[1]); " + "return 1; "+ "end; " + "return nil;", Arrays.<Object>asList(getName(), getChannelName()), LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(threadId));
解读:
第一个if:判断key=入参是否存在?若不存在(==0),广播机制,然后返回1。
第二个if:判断key和value是否存在?若不存在(==0 返回ni l。
脚本(存在):local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);获取value
第三个if:判断counter是否大于0?true-->value-1:
else:删除key并广播
while (true) { long currentTime = System.currentTimeMillis(); ttl = tryAcquire(leaseTime, unit, threadId); // lock acquired if (ttl == null) { return true; } ...... }
解读:
通过while(true)实现线程等待
3.1 简介:在分布式或者多线程场景下,为了让一个数据或方法(数据来源数据库)在同一时刻只能让一个人处理(即串性),我们可以利用Redission锁让只有拿到锁的人才能编辑该数据。
实现原理:利用切面编程+注解(RedissionLock)编程的方式实现对方法上加入了RedissionLock注解的方法进行全局加锁
3.2 定义注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RedissionLock { /** * 锁住方法中的第几个参数(-1全部参数) * * @return */ int lockIndexParam() default -1; /** * 锁的时间 * * @return */ int leaseTime() default 10; /** * 等待时间 */ int waitTime() default 5; }
3.3 注册切面
@Aspect @Component public class RedissionLockAspect { @Resource private RedissonClient redissonClient; /** * 环绕通知:灵活自由的在目标方法中切入代码 */ @Around("@annotation(redissionLock)") public Object around(ProceedingJoinPoint joinPoint, RedissionLock redissionLock) throws Throwable { // 获取目标方法的名称 String methodName = joinPoint.getSignature().getName(); // 获取方法传入参数 Object[] params = joinPoint.getArgs(); System.out.println("==@Around== lingyejun blog logger --》 method name " + methodName + " args " + params[0]); RLock lock = redissonClient.getLock(params[redissionLock.lockIndexParam()-1].toString()); boolean b = lock.tryLock(redissionLock.waitTime(), redissionLock.leaseTime(), TimeUnit.MINUTES); if (b) { lock.unlock(); return joinPoint.proceed(); } else { return null; } } }
3.4 使用方式
@RedissionLock(lockIndexParam = 1) public void tt(String id){ System.out.println("进入了方法::" + id); }