java 防止超卖_实现防止超卖的几种方式

//利用redis 做分布式锁,此处实现需要根据实际场景进行优化. 好处是能分担数据库压力,但加锁的时间无法确定,需要另启线程进行延时处理//有个问题是 如果子线程延期6次后 主线程还未运行完毕 后续又会引发很多问题,这里可以 结合case1的乐观锁一起使用,用version字段双重保险,或许本来就应该这么做

static ThreadPoolExecutor pool = new ThreadPoolExecutor(10,20, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(20));//这里最高并发限制50,我这里只有拿到锁了 才开启线程,早已有余,商品种类多,可以考虑加大队列/最大线程数

static{

pool.prestartAllCoreThreads();//预先创建核心线程

}

@GetMapping("/payII")public String testPayLockByRedis() throwsInterruptedException {//这里最好还是用userId

String name =Thread.currentThread().getName();for (int i = 0; i < 5; i++) {

Boolean lock= redisTemplate.opsForValue().setIfAbsent("1", name, 10000, TimeUnit.MILLISECONDS);if(lock) {

MyRunnable m= newMyRunnable(name);try{

pool.execute(m);

Goods goods= goodsMapper.selectByPrimaryKey(1);int storage =goods.getGoodsStorage();

goods.setGoodsStorage(storage- 1);if (goods.getGoodsStorage() >= 0) {

goodsMapper.updateByPrimaryKey(goods);

log.info("本次购买商品一件,剩余库存{}件", goods.getGoodsStorage());return "本次购买商品一件,剩余库存" + goods.getGoodsStorage() + "件";

}else{

log.info("库存不足");return "库存不足";

}

}catch(Exception e) {//这里抛出异常让事务回滚 , 异常部分让切面处理,优先级和事务一致,优先级一致事务先执行

throw newRunTimeException(e);

}finally{

m.flag= false;//这里的处理是因为 子线程在延时6次后,没有中断主线程的运行(这里无法中断,线程之间的运行是独立的,子线程抛出异常无法被主线程捕获,至多让thread设置一个//UncaughtExceptionHandler,在子线程抛出异常后,子线程内部自己进行捕获处理逻辑,然而还是不能影响主线程),既然如此,存在30s过后主线程仍未执行完毕的可能性,此时锁已易主,如果未//和version字段一起做保险处理,建议抛出异常,回滚事务

Object o = redisTemplate.opsForValue().get("1")if (o!=null &&name.equals(o.toString())) {

redisTemplate.delete("1");

}else{throw new RuntimeException("锁已失效")}

}

}else{continue;

}

}return "网络延迟,请稍后尝试";

}private class MyRunnable implementsRunnable {boolean flag = true;int time; //延期次数

String name; //线程name 用userId好些

private MyRunnable(String name) {this.name =name;}

@Overridepublic voidrun() {try{//给予一定的处理时间 再给任务做延时处理

Thread.sleep(2000);while (flag && time++ < 6) {

Object o= redisTemplate.opsForValue().get("1");//锁存在 且是自身加的锁 给锁延期

if (o != null && o.toString().equals(name) &&flag) {//如果出现判定通过

/*case1: 外面修改flag 这里延期成功 外面删除 并不影响

case2: 外面修改flag 删除 还未往redis 重新set 这里就延期 也不影响

case3: 外面修改flag 删除 其他用户往redis 重新set 这里再延期 好像也不会发生什么*/redisTemplate.expire("1", 10000, TimeUnit.MILLISECONDS);

System.out.println("延时一次");

}

Thread.sleep(3000);

}

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值