关于redisson锁的使用和理解

对于分布式锁的要求

  • 互斥性:分布式锁需要保证在不同节点的不同线程的互斥。这是最根本的
  • 可重入性:同一个节点上的同一个线程如果获取了锁之后也可以再次获取这个锁
  • 锁超时:和本地锁一样支持锁超时,防止死锁
  • 高可用:加锁和解锁需要高效,同时也需要保证高可用防止分布式锁失效,可以增加降级
  • 支持阻塞和非阻塞:和 ReentrantLock 一样支持 lock 和 trylock 以及 tryLock(long timeOut)
  • 支持公平锁和非公平锁(可选):公平锁的意思是按照请求加锁的顺序获得锁;非公平锁就相反是无序的,一般来说实现的比较少

关于redisson锁

其实我们都知道ReentrantLock已经有很好的锁的性能和实现,在互斥性、可重入性、锁超时、支持阻塞、支持公平锁都有很好的性能和实现,但不适用分布式场景。redisson是分布式锁,弥补这一缺憾(分布式锁有很多种,其他此文不做讨论),其中RLock接口继承了Lock接口,自然也会优雅的实现以上对锁的要求。

模拟并发测试

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedissLockUtilTest {

@Autowired
private RestTemplate restTemplate;

@Test
public void test() {
String helloworld = "helloworld";
ConcurrentTestUtil.test(helloworld, e -> {
ResponseEntity<String> forEntity = restTemplate
.getForEntity("http://localhost:8080/sendMessage?message="+Thread.currentThread().getName(), String.class);
String body = forEntity.getBody();
System.out.println(body);
}, 2); //并发2个线程测试
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@Slf4j
@RestController
@SpringBootApplication
@ComponentScan("cn.appblog")
public class SpringbootApplication {

@GetMapping("sendMessage")
public String sendMessage(String message, @RequestParam(required = false) Integer timeout) {
return doRedisson(message);
}

@Autowired
private RedissonClient redissonClient;

private String doRedisson(String message) {
RLock redLock = redissonClient.getLock("REDLOCK_KEY");
boolean tryLock = false;
try {
//tryLock = redLock.tryLock(2, 5, TimeUnit.SECONDS);
//if (!tryLock) return message;
redLock.lock(2, TimeUnit.SECONDS);
StopWatch stopWatch = new StopWatch();
stopWatch.start();
System.out.println(Thread.currentThread().getName());
countdown(5);
stopWatch.stop();
double totalTimeSeconds = stopWatch.getTotalTimeSeconds();
System.out.println(totalTimeSeconds);
return "success";
} catch (Exception e) {
e.printStackTrace();
} finally {
//if (redLock.isLocked()) //需要unlock前的判断
//if (redLock.isHeldByCurrentThread())
redLock.unlock();
}
return message;
}

private void countdown(Integer timeout) {
Random random = new Random();
int i = random.nextInt(timeout);
//int i = timeout;
while (i != 0) {
i--;
log.warn(String.valueOf(i));
try {
TimeUnit.SECONDS.sleep((long) 1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}

public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}

日志信息和分析

1
2
3
// 锁的释放时间
// 时间单位
void lock(long leaseTime, TimeUnit unit);

选择lock(2, TimeUnit.SECONDS);表示锁超过两秒锁即释放;但是第一个请求获取到锁,执行方法需要3秒,自然锁早已经超时释放。当第二个请求执行unlock就会报错。

1
2
3
4
5
6
7
8
2019-01-17 13:25:22.843  WARN 23556 --- [io-8080-exec-10] c.d.appblog.redis.SpringbootApplication  : 2
2019-01-17 13:25:23.844 WARN 23556 --- [io-8080-exec-10] c.d.appblog.redis.SpringbootApplication : 1
2019-01-17 13:25:24.845 WARN 23556 --- [io-8080-exec-10] c.d.appblog.redis.SpringbootApplication : 0
http-nio-8080-exec-10----3.002
2019-01-17 13:25:24.848 WARN 23556 --- [nio-8080-exec-9] c.d.appblog.redis.SpringbootApplication : 2
2019-01-17 13:25:25.849 WARN 23556 --- [nio-8080-exec-9] c.d.appblog.redis.SpringbootApplication : 1
java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: 74ca2ff2-34c1-49c2-9c1d-5543780c952b thread-id: 69
org.apache.catalina.connector.ClientAbortException: java.io.IOException: 远程主机强迫关闭了一个现有的连接。
1
2
3
4
// 线程的等待时间,其他超过即取消
// 锁的释放时间
// 时间单位
tryLock(long waitTime, long leaseTime, TimeUnit unit);

选择tryLock(2, 5, TimeUnit.SECONDS);表示线程等待超过两秒锁即取消等待;但是第一个请求获取到锁,执行方法需要3秒,自然超过等待时间。第二个请求会变被执行unlock报错。

1
2
3
4
5
2019-01-17 14:52:49.493  WARN 18544 --- [nio-8080-exec-6] c.d.appblog.redis.SpringbootApplication  : 2
2019-01-17 14:52:50.494 WARN 18544 --- [nio-8080-exec-6] c.d.appblog.redis.SpringbootApplication : 1
2019-01-17 14:52:51.495 WARN 18544 --- [nio-8080-exec-6] c.d.appblog.redis.SpringbootApplication : 0
java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: 2e9e88ca-a4e0-4c53-be05-98ed6a96ba83 thread-id: 62
http-nio-8080-exec-6----3.004

Powered by AppBlog.CN     浙ICP备14037229号

Copyright © 2012 - 2020 APP开发技术博客 All Rights Reserved.

访客数 : | 访问量 :