Java实现乐观锁和悲观锁

1,002 阅读2分钟

前言

  • 在数据库当中有乐观锁和悲观锁,在我们更新数据的时候:
    • 1.检索出更新数据,操作人员看;
    • 2.操作人员修改数据;
    • 3.保存更新数据

乐观锁:

  • 认为一般不会冲突
    • 1.数据提交时"是否有冲突检测"
    • 2.没冲突提交,反正报错,并自定义处理
  • mysql实现:
    • 1.在表中加个version字段
    • 2.取数据的时候连着version一起取出来
    • 3.更新的时候,比对version是否一直,如果不一致表示失败
    • 4.如果一致正常更新,version字段+1
  • 程序实现:
    • 1.在检索数据,将数据的版本号(version字段)检索出来;
    • 2.在数据库执行update操作时,用检索出来的版本号与数据库中的记录做比较;
    • 3.如果版本号一致就更新,反之就给出提示;

代码:

  • 1.增加一个方法:getUserCoinForModel 注意:返回的是 整个Model
//获取用户虚拟币方法 乐观锁
   public UserCoinModel getUserCoinForModel(String userName){
   	QueryWrapper<UserCoinModel> queryWrapper = new QueryWrapper<>();
   	queryWrapper.eq("username",userName);
   	return this.coinMapper.selectOne(queryWrapper);
}
  • 2.增加一个方法:updateVersion
//获取用户虚拟币方法 乐观锁
   public UserCoinModel getUserCoinForModel(String userName){
   	QueryWrapper<UserCoinModel> queryWrapper = new QueryWrapper<>();
   	queryWrapper.eq("username",userName);

   	return this.coinMapper.selectOne(queryWrapper);

}
    1. 修改upgrade2方法 在第四部更新处检测是否有冲突,可以抛出异常不更新也可以更新一条默认数据。
	@Transactional(rollbackFor = Exception.class)
   public int upgrade2(String userName) throws Exception {//升级用户
   	//第一步:查询用户 虚拟币
   	UserCoinModel userCoinModel = this.coinService.getUserCoinForModel(userName);
   	int getCoin=userCoinModel.getCoin();
   	int getVersion = userCoinModel.getVersion();//版本号
   	//第二步:判断用户级别
   	USER_LEVEL user_level = USER_LEVEL.BRONZE;//默认青铜
   	if(getCoin>=500 && getCoin<=1000){
       user_level=USER_LEVEL.GOLD;//黄金用户
   	}else if(getCoin>1000){
       	user_level=USER_LEVEL.PLATINUM;//铂金用户
   	}
   	//第三步:插表
   	UserLevelModel userLevelModel = new UserLevelModel();
   	userLevelModel.setUserName(userName);
   	userLevelModel.setUserLevel(user_level.value());
   	//第四部:更新user_coin 表的version
   	if(this.coinService.updateVersion(userName,getVersion)!=1){
       	throw new Exception("升级失败");
   	}
   	return this.userLevelMapper.upgrade(userLevelModel);
}

悲观锁

  • 总是最坏的情况:
    • 1.每次拿数据都认为别人会修改数据,所以要加锁;
    • 2.别人只能等待,直到释放锁才能拿到锁;
    • 3.数据库的行锁、表锁都是这种方式;
    • 4.悲观锁适用于读相对少,写相对多的操作;
  • mysql实现
    • 1.InnoDB默认是行级别的锁,当然必须命中索引或者主键(主键索引),是行级锁。否则是表级别。
  • sql语句
select * from xxx where username for update

代码

	public int getUserCoin(String userName){
   	QueryWrapper<UserCoinModel> queryWrapper = new QueryWrapper<>();
   	queryWrapper.eq("username",userName);
   	queryWrapper.last("for update");
   	//select * from xxx where username for update
   	UserCoinModel userCoinModel = this.coinMapper.selectOne(queryWrapper);
   	if(userCoinModel==null){
       	return 0;
   	}else {
       	return userCoinModel.getCoin();
   	}
   }