方法一:将方法设为同步方法
效率低
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