我学长要求我在学校的小程序里做一个组队的功能,学生发帖可以发起组队贴,这样可以找到同好 下面是我写的方法的代码,学长希望将 synchronized 代码块改成双重检查锁的形式,我看了我以前的笔记,发现双重检查锁一般是用于单例模式中的,而不知道怎么将这个类改成单例模式的样子,实在不会了来问问
@CrossOrigin @RestController @Slf4j @RequestMappin("/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 yemoluo 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 我理解的双重检验是在正逻辑里做容错,你用反逻辑直接返回的思路和这个模式是反的 |