悲观锁主要是在数据库中使用,数据库的默认事务隔离级别为可重复读。
乐观锁的实现分为数据库实现和缓存实现。
数据库实现:
缓存实现:
关于cas(compareAndSwap)指的就是乐观锁的实现(version类型的实现)
update goods
set account=account-#{account},version=version+1
where code=#{code} and version=#{version}
update goods
set account=account-#{account}
where code=#{code} and account-#{account}>0
service代码如下:
@Override
//@Transactional 添加事务也不行 数据库事务默认为可重复读 会有多个线程执行了goodsMapper.selectById(code) 然后update的时候使用的是旧数据,导致数据错误
public boolean updateGoodsAccountNoControl(String code, int account) {
Goods goods = goodsMapper.selectById(code);
System.out.println("goods:"+goods);
if(goods==null||goods.getAccount() < account){
System.out.println("返回false 1");
return false;
}
return goodsMapper.updateGoodsAccountNoControl(code,account) > 0 ? true:false;
}
updateGoodsAccountNoControl 如下
@Update("update goods set account=account-#{account} where code=#{code}")
int updateGoodsAccountNoControl(@Param("code")String code,@Param("account") int account);
这种方式实现实现的没有任何控制的逻辑在并发请情况下回导致account出现负数的情况。
service代码如下
@Override
public boolean updateGoodsAccountVersionControl(String code, int account) {
Goods goods = getById(code);
if(goods==null||goods.getAccount() < account){
return false;
}
//第一次1000个请求过来 只有一个请求会成功 其他的请求都会失败(version不匹配)
if(goodsMapper.updateGoodsAccountVersionControl(code,account,goods.getVersion()) > 0 ){
return true;
}
//更新失败可能是版本号已经改变了 此时应该重试而不是直接说失败
//等待一个随机的时间 错峰
waitForLock();
return updateGoodsAccountVersionControl(code,account);
}
updateGoodsAccountVersionControl方法
@Update("update goods set account=account-#{account},version=version+1 where code=#{code} and version=#{version}")
int updateGoodsAccountVersionControl(@Param("code") String code, @Param("account")int account,@Param("version") Integer version);
waitForLock方法
private void waitForLock() {
Random random = new Random();
try {
Thread.sleep(random.nextInt(10) + 1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
该方式可保证库存正确
service方法
@Override
public boolean updateGoodsAccountStateControl(String code, int account) {
Goods goods = getById(code);
if(goods==null||goods.getAccount() < account){
return false;
}
if(goodsMapper.updateGoodsAccountStateControl(code,account) > 0 ){
return true;
}
//等待一个随机的时间 错峰
waitForLock();
return updateGoodsAccountStateControl(code,account);
}
goodsMapper.updateGoodsAccountStateControl方法
@Update("update goods set account=account-#{account} where code=#{code} and account-#{account}>0")
int updateGoodsAccountStateControl(@Param("code")String code,@Param("account") int account);
该方式可保证库存正确