• 高并发操作同一个数据造成错误逻辑数据问题


    高并发操作同一个数据会产生数据超库的问题,也就是本来商品剩余量最小应该是0,但是由于高并发可能引起剩余量成为负数,订购表中关系也会增加很多,最终就是商品没了,但是客户全部显示订购成功。
    最开始商品数据是10,订购表为空。
    但是若按照寻常的代码逻辑走,如下:
    这个是dao层代码,更新商品库存数量的方法。
     public int reduceNum1(int num,String id){
    String sql = "update SysKc t set t.num=(t.num-?) where t.id=? ";
     
    //Integer i = this.execute(sql,num,id);
    return this.execute(sql,num,id);
     
      }
    这个是controller层调用的方法。
     public String SecKill() throws InterruptedException {
    try {
      // String consumer = request.getParameter("consumer");
      String goodId = request.getParameter("goodsId");
      int nums = Integer.parseInt(request.getParameter("num"));
      for(int i=0;i<50;i++){
      final String consumer="consumer"+String.valueOf(i);
      final String goodsId=goodId;
      final int num=nums;
      new Thread(new Runnable() {
     
    @Override
    public void run() {
    try{
    // TODO Auto-generated method stub
    //查找出用户要买的商品
    SysKc goods = sysKcService.findById(goodsId);
    //如果有这么多库存 -----重点在这判断,如果高并发会产生错误
    if(goods.getNum()>=num){
    //模拟网络延时
    // Thread.sleep(1000);
    //先减去库存
    // int a = sysKcService.reduceKc(num, goodsId);
     
    sysKcService.reduceKc(num, goodsId);
    sysKcOrderService.generateOrder(consumer, goodsId, num);
    System.out.println(consumer+":购买成功");
     
    //保存订单
    // if(a!=0){
     
    // }
    }else{
    System.out.println(consumer+":购买失败");
     
    }
    }catch(Exception e){
    e.printStackTrace();
    }
    }
    }).start();
     
      }
      System.out.println("结束");
    } catch (Exception e) {
    e.printStackTrace();
    }
      return null;
    }
     
    如果按照以上的调用方法和dao层更新商品库存数量的方法设计代码,则出现的结果是商品表中库存成为负数,订购表中出现大于库存的订购关系。
    解决思路:
    第一,利用数据库的行级锁。 就是利用Oracle,MySQL的行级锁–同一时间只有一个线程能够操作同一行记录,对Dao层更新库存的方法进行改造:
     public int reduceNum1(int num,String id){
    String sql = "update SysKc t set t.num=(t.num-?) where t.id=? and t.num>0 "; //---加上一句 and t.num>0,并且在控制层对返回的int值进行判断。
     
    //Integer i = this.execute(sql,num,id);
    return this.execute(sql,num,id);
     
      }
     public String SecKill() throws InterruptedException {
    try {
      // String consumer = request.getParameter("consumer");
      String goodId = request.getParameter("goodsId");
      int nums = Integer.parseInt(request.getParameter("num"));
      for(int i=0;i<50;i++){
      final String consumer="consumer"+String.valueOf(i);
      final String goodsId=goodId;
      final int num=nums;
      new Thread(new Runnable() {
     
    @Override
    public void run() {
    try{
    // TODO Auto-generated method stub
    //查找出用户要买的商品
    SysKc goods = sysKcService.findById(goodsId);
    //如果有这么多库存
    if(goods.getNum()>=num){
    //模拟网络延时
    Thread.sleep(1000);
    //先减去库存
    int a = sysKcService.reduceKc(num, goodsId);
    //保存订单
    if(a!=0){ //重点在这行和上面的一行上。
    sysKcOrderService.generateOrder(consumer, goodsId, num);
    System.out.println(consumer+":购买成功");
    }
    }else{
    System.out.println(consumer+":购买失败");
     
    }
    }catch(Exception e){
    e.printStackTrace();
    }
    }
    }).start();
     
      }
      System.out.println("结束");
    } catch (Exception e) {
    e.printStackTrace();
    }
      return null;
    }
    第二种方法利用同步锁的方式,在操作商品表和订购关系的方法上加锁。
     
     
     
     public String SecKill() throws InterruptedException {
    try {
      // String consumer = request.getParameter("consumer");
      String goodId = request.getParameter("goodsId");
      int nums = Integer.parseInt(request.getParameter("num"));
      for(int i=0;i<50;i++){
      final String consumer="consumer"+String.valueOf(i);
      final String goodsId=goodId;
      final int num=nums;
      new Thread(new Runnable() {
     
    @Override
    public void run() {
    try{
    // TODO Auto-generated method stub
    //查找出用户要买的商品
    order(goodsId,consumer,num);
     
    }catch(Exception e){
    e.printStackTrace();
    }
    }
    }).start();
     
      }
      System.out.println("结束");
    } catch (Exception e) {
    e.printStackTrace();
    }
      return null;
    }
    //加锁
    public synchronized void order(String goodId,String consumer,int num) throws Exception{
     
    SysKc goods = sysKcService.findById(goodId);
    if(goods.getNum()>=num){
    sysKcService.reduceKc(num, goodId);
      sysKcOrderService.generateOrder(consumer, goodId, num);
    System.out.println(consumer+":购买成功");
    }else{
    System.out.println(consumer+":购买失败");
     
      }
    }
     
    利用上述的方法处理后,不管网络延迟是否,结果都是商品库存最后减少为0,订购关系和原始库存的数量一致。同步锁比较耗资源,所以建议利用行锁。
  • 相关阅读:
    tomcat7
    SSO
    搜索服务Solr集群搭建 使用ZooKeeper作为代理层
    JavaScript
    JavaScript中给onclick绑定事件后return false遇到的问题
    ES6.0简单了解
    php之gennerator
    RBAC权限管理及使用原生PHP实现
    使用YII框架的migrate迁移数据库
    shell脚本--文件包含
  • 原文地址:https://www.cnblogs.com/wjwen/p/7326844.html
Copyright © 2020-2023  润新知