多线程场景 Redis中List队列 LPush 和 remove 是否线程安全?
1.业务场景如下
作者在rabbitmq
消息队列场景中,实现了一个监控消息数据全部入库的程序,每个生产者发送消息的同时,插入一个key
到 redis
中的 list 队列
中,消费成功之后,再从这个队列中删除该 key
值。
通过最后判断 redis
中的 list 队列
是否有数据,来判断消费是否成功。
其中 生产者 是并发场景,消费者 也是集群的并发消费。
2.猜想
从官网可知redis
服务端是单线程实现的,命令操作都是原子;所以虽然客户端是多线程访问,但是服务端接收之后,还是会使用单线程进行操作,所以应该是线程安全的,不会出现数据不一致的情况。
3.验证
3.1环境
- springboot2.0.4
- springmvc
3.2生产者代码
@GetMapping("/testRedisConcurrentLPush")
public @ResponseBody
String testRedisConcurrentLPush(HttpServletRequest request, Integer count) {
for (int i = 0; i < count; i++) {
new Thread(() -> {
for (int j = 0; j < 5; j++) {
redisUtil.lSet("test_msg_list_8a8781fe70f6f5cf0170f6f5cf970000",
"key_msg");
redisUtil.incr("test_msg_list_count", 1);
}
}).start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "成功";
}
3.3消费者代码
@GetMapping("/testRedisConcurrentLRemove")
public @ResponseBody
String testRedisConcurrentLRemove(HttpServletRequest request, Integer count) {
for (int i = 0; i < count; i++) {
new Thread(() -> {
for (int j = 0; j < 5; j++) {
redisUtil.lRemove("test_msg_list_8a8781fe70f6f5cf0170f6f5cf970000",
1, "key_msg");
redisUtil.decr("test_msg_list_count", 1);
}
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "成功";
}
4.测试
开两个 tab窗口 浏览器分别输入
- 生产者多线程循环添加
http://127.0.0.1:8098/democratic/testRedisConcurrentLPush?count=20
- 消费者多线程循环删除
http://127.0.0.1:8098/democratic/testRedisConcurrentLRemove?count=20
4.1测试结果:
-
test_msg_list_count 计算为 0
-
队列数据为0,元素全部删除之后,队列消失
如果 生产者 count 设置20,消费者设置19
- test_msg_list_count 计算为 5
- 队列数据为5条
多次测试结果均符合预期,即线程安全。
如果有更加正确的验证方式 或者 本文结论证实错误,欢迎指正。