java 针对抢购功能对比一下悲观锁和乐观锁

抢购功能:在极短的时间内有大量的并发请求,出现因为并发所引起的问题。

实例:

一个商品50件,一个用户一次购买3件,100个用户同时购买。

设计:

mysql数据库

建表语句:

CREATE TABLE `product` (
  `id` int(11) NOT NULL,
  `product_id` int(11) DEFAULT NULL,
  `stock` int(11) DEFAULT NULL COMMENT '库存',
  `version` int(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `product_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_num` int(11) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5739 DEFAULT CHARSET=utf8;

前端模拟并发请求:

function  aaa(){
    for (var i = 1; i<=100;i++){
        $.post("http://localhost:8081/xxx/xxx",function (result) {
        });
    }
}
</script>

并发量测试 <button id="but" onclick="aaa()">启动</button>

测试两组数据:

产品数为50,每个用户买3,100用户同时抢购(在系统并发内),系统并发为21,也就是说最多处理63个产品
产品数为100,每个用户买3,100用户同时抢购(在系统并发外)

并发量和电脑cpu和内存以及jvm等有关,未使用连接池,目前无法得知和mysql的连接数是否有关。

第一种情况:不用锁

  @Transactional
    public void common(int num){
        Product product =productDao.selectOne(new QueryWrapper<Product>().eq("id",1));
        if (product.getStock()>=num){
            product.setStock(product.getStock()-num);
            ProductInfo productInfo=new ProductInfo();
            productInfo.setProductNum(num);
            productInfo.setCreateTime(LocalDateTime.now());
            productDao.updateById(product);
            productInfoDao.insert(productInfo);
        }
    }

结果:

50:产品表剩余2,用户购买表77个订单,超发现象。
100:产品表剩余37,用户购买表100个订单,出现线程不安全问题。

第二种情况:悲观锁

悲观锁直接去操作sql语句,在最后面加上 for update即可。

   @Transactional
    public void beiguan(int num){
        Product product =productDao.selectOne(new QueryWrapper<Product>().eq("id",1).last("for update"));
        if (product.getStock()>=num){
            product.setStock(product.getStock()-num);
            ProductInfo productInfo=new ProductInfo();
            productInfo.setProductNum(num);
            productInfo.setCreateTime(LocalDateTime.now());
            productDao.updateById(product);
            productInfoDao.insert(productInfo);
        }
    }

结果:

50:产品表剩余2,用户购买表16个订单,发放正常,会影响效率。
100:产品表剩余1,用户购买表33个订单,发放正常,会影响效率。

第三种:乐观锁

 @Transactional
    public void leguan(int num){
        for (int i=0;i<3;i++){
            Product product  = productDao.getStock();
            if (product.getStock()>=num){
                int flag =productDao.updStock(product.getVersion());
                if (flag==0){
                    continue;
                }
                ProductInfo productInfo=new ProductInfo();
                productInfo.setProductNum(num);
                productInfo.setCreateTime(LocalDateTime.now());
                productInfoDao.insert(productInfo);
                return;
            }

        }
    }

结果:

50:产品表剩余2,用户购买表16个订单,发放正常。
100:产品表剩余40,用户购买表20个订单,发放不正常,数据正常,存在请求失败情况。

总结:

1.select * from a for update 会加上悲观锁,只能被一个资源持有,做什么都上锁。效率低,有时候称为排它锁,独占锁

2.乐观锁时在sql语句加上一个只增不减得版本号,判断版本号是否一致,一致则修改,不一致则是其他线程修改了,取消这次操作。

3.乐观锁还可以引入重入机制,一旦失败就重新做一次。称为可重入锁。

4.可重入锁避免多次给予数据库压力,限制时间或者次数,超过时间限制就不再重入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值