ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CAS乐观锁和悲观锁

CAS乐观锁和悲观锁 一、CAS乐观锁乐观锁认为线程安全问题不一定会发生因此不加锁只是在个更新数据时去判断有没有其他线程对数据做了修改如果没有修改则认为是安全的自己才更新数据如果已经被其他线程修改说明发生了安全问题此时可以重试或异常CAS乐观锁主要解决资源数量竞争问题Compare And Swap核心更新数据时不仅修改数据还要验证当前状态是否满足条件典型场景库存超卖多个用户线程同时购买库存stock超卖UPDATE product SET stock stock - 1 WHERE id 1 AND stock 0只有满足库存 stcok 0才允许扣减Java代码// 扣减库存CAS乐观锁 boolean success seckillVoucherService .update() .setSql(stock stock - 1) .eq(voucher_id, voucherId) .gt(stock,0) //核心 .update(); if(!success){ return Result.fail(库存不足); }二、悲观锁悲观锁认为线程安全问题一定会发生因此在操作数据之前先获得锁确保线程串行执行例如Synchronized、Lock都属于悲观锁典型场景一个用户只能购买一次一个用户只能领取一次新人礼包一个账号只能参与一次活动连续相同请求发送只有一个请求成功其他请求拒绝一个用户购买商品过程查询库存是否还有创建订单数据问题查询和创建不是原子操作中间有时间窗口如果用户快速请求多次就会创建多个订单核心目标让同一个业务对象例如同一个用户的关键业务流程同一时间只能有一个线程执行例如查询库存 创建订单使用悲观锁实现一人一单最简单的思想就是给用户加锁例如一个用户只能购买一次则锁住用户对象例子Long userId UserHolder.getUser().getId(); String lockKey user: userId; synchronized(lockKey.intern()){ // 查询订单 // 判断是否存在 // 创建订单 }不能锁userID因为两个请求可能产生两个不同的Long对象锁住的不是同一个对象所以使用字符串常量池重点intern()作用让相同字符串指向同一个对象注意这里用悲观锁实现的场景是单机模式在服务器集群中一个JVM只有一个锁监视器多个JVM锁的对象不同需要使用Redis分布式锁三、一个用户领取秒杀优惠券案例完整代码使用乐观锁与悲观锁Override Transactional public Result seckillVoucher(Long voucherId){ //1.查询优惠券 SeckillVoucher voucher seckillVoucherService.getById(voucherId); //2.判断秒杀时间 if(voucher.getBeginTime() .isAfter(LocalDateTime.now())){ return Result.fail(未开始); } if(voucher.getEndTime() .isBefore(LocalDateTime.now())){ return Result.fail(已结束); } //3.判断库存 if(voucher.getStock() 1){ return Result.fail(库存不足); } // 悲观锁 Long userId UserHolder.getUser().getId(); String lockKey order: userId; synchronized(lockKey.intern()){ //4.查询用户是否已经购买 int count query() .eq(user_id,userId) .eq(voucher_id,voucherId) .count(); if(count 0){ return Result.fail( 不能重复购买 ); } // 乐观锁 //5.扣库存 boolean success seckillVoucherService .update() .setSql(stockstock-1) .eq(voucher_id,voucherId) .gt(stock,0) .update(); if(!success){ return Result.fail( 库存不足 ); } //6.创建订单 VoucherOrder order new VoucherOrder(); order.setUserId(userId); order.setVoucherId(voucherId); save(order); return Result.ok(order.getId()); } }
返回列表