超卖问题的乐观锁与悲观锁
悲观锁认为线程安全问题一定会发生因此在操作数据之前先获取锁确保线程串行执行。例如Synchronized、Lock都属于悲观锁认为线程安全问题一定会发生因此在操作数据之前先获取锁确保线程串行执行。乐观锁认为线程安全问题不一定会发生因此不加锁只在更新数据时去判断所以乐观锁只能在有更新操作的地方使用有没有其它线程对数据做了修改。如果没有修改则认为是安全的自己才更新数据。如果已经被其它线程修改说明发生了安全问题此时可以重试或异常一句话判断之前查询得到的数据是否有被修改过悲观锁就不多说了乐观锁有两种实现方式版本号法更新一次数据就更新一次版本号每次更新前判断版本号是否相同就可以确定是否更新过CAS法我的理解是设置一个预期值达到预期值后就不能继续操作了。以下有一个来自黑马点评的案例。我们采用了CAS法设置库存预期值为0当发现库存小于1时就要停止操作了。每个线程会在更新前进行判断Service public class VoucherOrderServiceImpl extends ServiceImplVoucherOrderMapper, VoucherOrder implements IVoucherOrderService { Resource private ISeckillVoucherService seckillVoucherService; Resource private RedisIdWorker redisIdWorker; Override Transactional public Result seckillVoucher(Long voucherId) { //查询优惠券 SeckillVoucher voucher seckillVoucherService.getById(voucherId); //判断秒杀是否开始 LocalDateTime beginTime voucher.getBeginTime(); LocalDateTime endTime voucher.getEndTime(); if(beginTime.isAfter(LocalDateTime.now())){ return Result.fail(秒杀尚未开始); } //判断秒杀是否结束 if(endTime.isBefore(LocalDateTime.now())){ return Result.fail(秒杀已结束); } //库存是否充足 if (voucher.getStock() 1) { return Result.fail(库存不足); } //扣减库存 boolean success seckillVoucherService.update() .setSql(stock stock - 1) .eq(voucher_id, voucherId) .gt(stock,0) .update(); if(!success){ return Result.fail(扣减库存失败); } //创建订单 VoucherOrder voucherOrder new VoucherOrder(); //订单id long orderId redisIdWorker.nextId(order); //用户id Long userId UserHolder.getUser().getId(); voucherOrder.setId(orderId); voucherOrder.setUserId(userId); //代金券id voucherOrder.setVoucherId(voucherId); save(voucherOrder); //返回订单id return Result.ok(orderId); }