我学长要求我在学校的小程序里做一个组队的功能,学生发帖可以发起组队贴,这样可以找到同好 下面是我写的方法的代码,学长希望将 synchronized 代码块改成双重检查锁的形式,我看了我以前的笔记,发现双重检查锁一般是用于单例模式中的,而不知道怎么将这个类改成单例模式的样子,实在不会了来问问
@CrossOrigin
@RestController
@Slf4j
@RequestMapping("/planet")
public class TeamController implements Teamable {
@Resource
MongoTemplate mongoTemplate;
@Autowired
ZKCourseCalling courseCalling;
private Runtime runtime;
@Override
@ApiOperation("加入组队的帖子")
@PostMapping("/teamUpWith")
public R teamUp(@RequestParam String planetId, @RequestParam("weChat") String weChat){
String openId = UniversityInterceptor.getOpenId();
R userInfo = courseCalling.getUserInfo(openId);
Object data = userInfo.getData().get("userInfo");
User user = JSON.parseObject(JSON.toJSONString(data), User.class);
// user = new User();
// user.setOpenid("test");
// user.setAvatarUrl("test");
Planet planet = mongoTemplate.findById(planetId, Planet.class);
if(planet==null){
return R.error().message("没有找到该帖子");
}
Group group = planet.getGroup();
if(group.getInNum().equals(group.getTotalNum())){
return R.error().message("组队人数已满");
}
synchronized (this){
Member member = new Member();
member.setOpenId(user.getOpenid());
member.setAvatarUrl(user.getAvatarUrl());
member.setWechat(weChat);
List<Member> memberList = group.getMemberList();
memberList.add(member);
group.setMemberList(memberList);
group.setInNum(group.getInNum()+1);
mongoTemplate.updateFirst(Query.query(Criteria.where("_id").is(planetId)),Update.update("group",group), Planet.class);
}
return R.ok();
}
}
Planet 是帖子对象,Group 是组队属性,member 是存储具体组队对象的属性,注释的三行代码是创建 User 对象用于测试的代码,可以无视,synchronized 代码块上做的事就是取得帖子对象并做判断,代码块中往帖子对象中的 Group 属性赋予对应值最后更新数据库,也就是正式的加入组队方法。 就这些了,在线等 dalao 救一救
1
yeqizhang 2023-01-06 22:47:20 +08:00 via Android
可能是让你在 synchronized 中再查一次判断一次组队人数满没满?就是只有锁住的代码块会改变数量,在当前线程拿到锁时,上一个拿到锁的线程已经将组队人数搞满的。你可以多同时请求一下加入组队,会看到数据库中组队字段的人数超出设置的限制了。
|
2
TWorldIsNButThis 2023-01-06 23:07:57 +08:00
没用过 mongodb
mongodb 没有行锁之类的吗 为啥要代码锁 锁 this 不就是全局都限制了,然而 planetId 不同的时候并不需要锁 |
3
TWorldIsNButThis 2023-01-06 23:19:05 +08:00
双重锁的话,本质是拿到锁后要操作的东西不一定是最新状态,所以要再临界区里再读取一次,获取操作对象最新的状态并再次校验
你这个例子里就是两个人同时拿到一个 group , a 上锁,加入,保存,解锁 然后 b 再上锁,但是 b 操作的 group 对象还是 a 保存前的那个 group ,并不知道 a 已经加入并保存了, 所以要再读取一次数据库拿到最新的 group 再进行操作 |
4
GTim 2023-01-07 09:21:22 +08:00
用 redis incr... +1 大于总人数,那么就放弃。
|
6
Alan0000 2023-01-07 11:01:59 +08:00
多个线程操作数据库的时候,组队人数可能会多出,应该是在 sync 里面再写个"组队人数已满"的判断吧
|
7
tiRolin OP @TWorldIsNButThis 谢谢你,你的回答完美解决了我的问题,当时做完了不知道行不行,现在确定没问题了,我才回复你的,所以有些晚
|
8
z9ln 2023-01-18 10:56:29 +08:00
我理解的双重检验是在正逻辑里做容错,你用反逻辑直接返回的思路和这个模式是反的
|