我的这个加入组队帖子的方法如何改造成双重检查锁形式?

2023-01-06 21:33:07 +08:00
 tiRolin

我学长要求我在学校的小程序里做一个组队的功能,学生发帖可以发起组队贴,这样可以找到同好 下面是我写的方法的代码,学长希望将 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 救一救

1422 次点击
所在节点    Java
8 条回复
yeqizhang
2023-01-06 22:47:20 +08:00
可能是让你在 synchronized 中再查一次判断一次组队人数满没满?就是只有锁住的代码块会改变数量,在当前线程拿到锁时,上一个拿到锁的线程已经将组队人数搞满的。你可以多同时请求一下加入组队,会看到数据库中组队字段的人数超出设置的限制了。
TWorldIsNButThis
2023-01-06 23:07:57 +08:00
没用过 mongodb
mongodb 没有行锁之类的吗
为啥要代码锁 锁 this 不就是全局都限制了,然而 planetId 不同的时候并不需要锁
TWorldIsNButThis
2023-01-06 23:19:05 +08:00
双重锁的话,本质是拿到锁后要操作的东西不一定是最新状态,所以要再临界区里再读取一次,获取操作对象最新的状态并再次校验

你这个例子里就是两个人同时拿到一个 group ,
a 上锁,加入,保存,解锁
然后 b 再上锁,但是 b 操作的 group 对象还是 a 保存前的那个 group ,并不知道 a 已经加入并保存了,
所以要再读取一次数据库拿到最新的 group 再进行操作
GTim
2023-01-07 09:21:22 +08:00
用 redis incr... +1 大于总人数,那么就放弃。
GTim
2023-01-07 09:21:37 +08:00
@GTim 不然就用楼上的
Alan0000
2023-01-07 11:01:59 +08:00
多个线程操作数据库的时候,组队人数可能会多出,应该是在 sync 里面再写个"组队人数已满"的判断吧
tiRolin
2023-01-16 16:36:34 +08:00
@TWorldIsNButThis 谢谢你,你的回答完美解决了我的问题,当时做完了不知道行不行,现在确定没问题了,我才回复你的,所以有些晚
z9ln
2023-01-18 10:56:29 +08:00
我理解的双重检验是在正逻辑里做容错,你用反逻辑直接返回的思路和这个模式是反的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/907091

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX