最近在实现博客系统的markdown文章导入功能的时候,遇到了多并发的问题,主要是导入插件是element-plus提供的,然后进行批量上传的时候,实际上是同一时间发送多次上传请求,也就出现了多并发的情况。本以为使用synchronized就可以处理的,结果还是一个知识点!!!
相关代码
public MlogTags saveIfAbsent(@NotNull(message = "标签名称不能为空") String tagName) {
//从数据库获取标签,判断标签是否存在,如果不存在就新增,否则返回存在的标签信息
Optional<MlogTags> tags = getByName(tagName);
MlogTags tag;
if (tags.isEmpty()) {
tag = new MlogTags();
tag.setTag(tagName);
tag.setColor(Constant.DEFAULT_COLOR);
save(tag);
} else {
tag = tags.get();
}
return tag;
}
上面的代码会导致当多并发出现的时候,就会出现save方法还未执行,另一个请求已经执行完getByName这个方法,然后就会保存多份相同的标签。
考虑到是博客系统,直接使用synchronized加锁即可。
//加上synchronized
public synchronized MlogTags saveIfAbsent(@NotNull(message = "标签名称不能为空") String tagName) {
//从数据库获取标签,判断标签是否存在,如果不存在就新增,否则返回存在的标签信息
Optional<MlogTags> tags = getByName(tagName);
MlogTags tag;
if (tags.isEmpty()) {
tag = new MlogTags();
tag.setTag(tagName);
tag.setColor(Constant.DEFAULT_COLOR);
save(tag);
} else {
tag = tags.get();
}
return tag;
}
本以为加上就可以搞定了,但是问题依旧存在,锁没有生效。
具体原因
因为我是其他的方法在调用saveIfAbsent这个方法。因为导入操作要保证一致性,所以开启了事务管理,spring事务管理会在方法执行前生成一个代理类,通过代理类去执行,所以导致每次加锁,加的是每次生成的代理类上面的代码块。
@Override
@Transactional(rollbackFor = Exception.class)//开启aop事务管理
public Boolean importMarkdown(MultipartFile file, HttpServletRequest request) {
//解析md生成文章对象
ArticleDTO article = parseMdFile(file);
//保存文章标签信息
saveIfAbsent(article.getTag());
......其他业务代码
return true;
}
处理方式
在开启事务前加锁即可,即调用importMarkdown方法之前,进行加锁即可
synchronized (this) {
articleService.importMarkdown(file, request);
}
参考资料
Spring事务管理下synchronized锁失效问题
|