多线程使用redis进行累加结果不对,不能保证原子性解决方案
多线程使用redis进行累加结果不对解决方案
·
多线程使用redis进行累加结果不对,不能保证原子性解决方案
问题背景
在使用多线程进行对redis的value做累加时导致出错,使用伪代码进行原因分析,不能保证操作redis的原子性
for(int i=0;i<10;i++){
//提交10个线程,每个params装有10条数据
asyncServiceExecutor.submit(() -> submitSingle(globalId, params));
}
public Map<String,String> submitSingle(String globalId, JSONArray params) {
try {
log.info("params: {}, size: {}", params, params.size());
Map<String,String> resultVOlist = QueryController.doQuery(params);
//由于是多核,可以做到真正意义上同时间的并发,因此可能会出现有5个线程同时到达了这条语句
//第一次同时达到,redis里面就是没有这个globalId关键字,因此5个线程都是为null
Object commitObject = redisUtils.get(globalId);
//当有5个为null的线程进来之后,就会导致多次创建了同一个键,累加数据相当于丢失了4次,也就是40条
if (commitObject == null) {
boolean flag = redisUtils.set(globalId, params.size());
} else {
redisUtils.increment(globalId, (long) params.size());
}
return resultVOlist;
} catch (Exception e) {
log.error("QueryController not found error", e);
return new HashMap<>();
}
}
解决方案
1 既然是多线程产生的原因,那么我们可以把写redis的写操作移出到主线程,redis的底层写操作是单线程的
for(int i=0;i<10;i++){
//提交10个线程,每个params装有10条数据
Future<Map<String,String>> futureResult = asyncServiceExecutor.submit(() -> submitSingle(globalId, params));
Map<String,String> resultVOList = futureResult.get();
if(resultVOList != null){
Object commitObject = redisUtils.get(globalId);
if (commitObject == null) {
boolean flag = redisUtils.set(globalId, params.size());
} else {
redisUtils.increment(globalId, (long) params.size());
}
}
}
public Map<String,String> submitSingle(String globalId, JSONArray params) {
try {
log.info("params: {}, size: {}", params, params.size());
Map<String,String> resultVOlist = QueryController.doQuery(params);
return resultVOlist;
} catch (Exception e) {
log.error("QueryController not found error", e);
return new HashMap<>();
}
}
2 可以在线程里面把写redis的逻辑进行整体加锁
public Map<String,String> submitSingle(String globalId, JSONArray params) {
try {
log.info("params: {}, size: {}", params, params.size());
Map<String,String> resultVOlist = QueryController.doQuery(params);
if(redission.lock(uuid)){
Object commitObject = redisUtils.get(globalId);
if (commitObject == null) {
boolean flag = redisUtils.set(globalId, params.size());
} else {
redisUtils.increment(globalId, (long) params.size());
}
}
return resultVOlist;
} catch (Exception e) {
log.error("QueryController not found error", e);
return new HashMap<>();
}
}
总结
- 问题只会越来越多,但也要耐心解决
作为程序员第 115 篇文章,每次写一句歌词记录一下,看看人生有几首歌的时间,wahahaha …
Lyric: 如果难过请你忘了我
这是第4首歌,已经完结了,你们猜出歌名了吗?
- 歌名:借口
- 歌手:周杰伦
- 歌词:周杰伦
- 专辑:七里香
更多推荐
已为社区贡献25条内容
所有评论(0)