<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的腳本java
第一個if:判斷key = helloRedissionLock是否存在?若不存(==0)在則經過hset命令設置 key = helloRedissionLock的hash對象(key=當前線程ID:1 value=1),而且設置過時時間redis
第二個if:判斷key = helloRedissionLock和hash對象(key=當前線程ID:1 value=1)?若存在(==1)則:1.設置hash對象的value+1,2.從新設置過時時間spring
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));
解讀:springboot
第一個if:判斷key=入參是否存在?若不存在(==0),廣播機制,而後返回1。多線程
第二個if:判斷key和value是否存在?若不存在(==0 返回ni l。分佈式
腳本(存在):local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);獲取valueui
第三個if:判斷counter是否大於0?true-->value-1:lua
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); }