方法一:将方法设为同步方法

效率低

public void synchronized insertOrUpdate(User user){

}

方法二:双重检查,加上乐观锁

public void insertOrUpdate(User user){
	int updateNum= update(user);
	if(updateNum==0){
		synchronized (this) {
			updateNum= update(user);
			if (updateNum== 0) {// 双重检查锁定: 防止被多线程的情况下初始化多次,导致插入多次
				insert(user);
			}
		}
	}
} 
//乐观锁实现(Compare-And-Swap,CAS)
public int update(User user){
	int updated=0;
	do{
		User u=dao.loadById(user.getId());//先查询用户,假设此时用户余额为100
		if(u==null){
			break;
		}else{
			u.getQuery().addCondition(User.Field.id,u.getId());
			u.getQuery().addCondition(User.Field.amout,u.getAmout());
			u.setAmout(u.getAmout()-50);//扣款50元
			updated=dao.update(u);
			//update user set amout=(100-50) where id=xx and amout=100;
			//显然,只有当扣款前的数值和预期的一样的时候,扣款才会发生
			//否则会再到数据库中查询出最新的值,重新扣款。
			//例如,当多个线程查询出来余额都是100时,有一个线程将余额加了50
			//此时执行update user set amout=(100-50) where id=xx and amout=100语句将会更新0条。
			//之后会再查询出最新的值重新扣款
			//update user set amout=(150-50) where id=xx and amout=150
		}	
	
	}while(updated==0);
	return updated;
}
//更简单的方式,查询和更新一起操作
public int update2(User user){
	String sql="update userbalance set amount = amount - 50 where id="+user.getId();
	int updated=dao.doSql(sql);
	return updated;
}
//使用orm完成update2的功能
public int update3(User user){
	User u = new User();
	u.setId(user.getId());
	u.prepareUpdate(UserField.amout, new JpqlExpression("amout-50"));
	int updated = dao.update(u);
	return updated;
}

方法三:在查询的时候锁表(基于性能考虑,即便在事务中,一般也不会在一个普通的select语句中锁表。)


标题:并发情况下,查询数据后插入或更新(使用乐观锁更新)
作者:xingzhegu
地址:https://www.fxg.life/articles/2019/05/08/1557292062979.html