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

高并发操作同一个数据会产生数据超库的问题,也就是本来商品剩余量最小应该是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,订购关系和原始库存的数量一致。同步锁比较耗资源,所以建议利用行锁。

转载于:https://www.cnblogs.com/wjwen/p/7326844.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值