最近遇到比较多数据不一致的问题,大多数都是因为并发请求时,没及时处理的原因,故用一个比较有代表性的业务场景【活动秒杀】来模拟一下这个这种高并发所产生的问题。

众所周知,电商系统的秒杀活动是高并发的很好应用场景,这里用的demo模拟的基本框架是springBoot+mybatis+redis+mysql,搭建的过程,我这里就不提了,有需要的可以自行百度。

1.搭好的项目目录:

2.建了一张表(记录商品名称、本次可秒杀的库存量):

加了一条记录(后面每次测试都先手动把库存恢复成100才进行测试)

3.实体:

package com.mybatis.model;public class MiaoShaGoods {private Integer id;private String goodsName;private Integer goodsSum;private Integer version;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getGoodsName() {return goodsName;}public void setGoodsName(String goodsName) {this.goodsName = goodsName == null ? null : goodsName.trim();}public Integer getGoodsSum() {return goodsSum;}public void setGoodsSum(Integer goodsSum) {this.goodsSum = goodsSum;}public Integer getVersion() {return version;}public void setVersion(Integer version) {this.version = version;}
}

一、不做任何处理的高并发秒杀实现(错误演示):

1.Controller层,模拟500个并发调用:

package com.mybatis.controller;import com.mybatis.domain.BaseResponse;
import com.mybatis.domain.MiaoshaRequest;
import com.mybatis.service.MiaoshaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;@Controller
@RequestMapping(value="/miaoshagoods")
public class MiaoshaController {@Autowiredpublic MiaoshaService miaoshaService;@PostMapping("/miaosha_java_sql_lock")public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){BaseResponse response=new BaseResponse();for(int i=0;i<500;i++){Thread thread=new Thread(new Runnable() {@Overridepublic void run() {//不做任何处理的秒杀实现miaoshaService.miaoshaGoods(request,response);              }});thread.start();}return response;}
}

2.Service层,每个请求进来就去数据库里查剩余的库存量,并且抢购成功后,就减1个库存:

package com.mybatis.service.Impl;import com.mybatis.dao.MiaoShaGoodsDao;
import com.mybatis.domain.BaseResponse;
import com.mybatis.domain.MiaoshaRequest;
import com.mybatis.model.MiaoShaGoods;
import com.mybatis.service.MiaoshaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;@Service
public class MiashaServiceImpl implements MiaoshaService{@AutowiredMiaoShaGoodsDao miaoShaGoodsDao;private Lock lock = new ReentrantLock();@Autowiredprivate RedisTemplate<String,String> redisTemplate;/*** 不做任何处理的秒杀实现* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods(MiaoshaRequest request, BaseResponse response) {int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");}else{System.out.println("抢到iphoneX,失败!");}return response;}
}

3.dao层(mybatis的xml文件):

 <select id="getGoods" parameterType="java.lang.String" resultMap="BaseResultMap">select<include refid="Base_Column_List" />from miao_sha_goodswhere goods_name = #{goodsName,jdbcType=VARCHAR}</select><update id="updateMsGoods" parameterType="com.mybatis.model.MiaoShaGoods">update miao_sha_goodsset goods_sum = #{goodsSum,jdbcType=INTEGER}where goods_name = #{goodsName,jdbcType=VARCHAR}</update>

4.测试结果:

截图表明,居然有500个人抢购成功,而且库存量却只减少了12个,这是明显是错误的。

二、数据库乐观锁处理的高并发秒杀实现:

1.Controller层,模拟500个并发调用:

package com.mybatis.controller;import com.mybatis.domain.BaseResponse;
import com.mybatis.domain.MiaoshaRequest;
import com.mybatis.service.MiaoshaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;@Controller
@RequestMapping(value="/miaoshagoods")
public class MiaoshaController {@Autowiredpublic MiaoshaService miaoshaService;@PostMapping("/miaosha_java_sql_lock")public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){BaseResponse response=new BaseResponse();for(int i=0;i<500;i++){Thread thread=new Thread(new Runnable() {@Overridepublic void run() {//不做任何处理的秒杀实现//miaoshaService.miaoshaGoods(request,response);//数据库乐观锁秒杀miaoshaService.miaoshaGoods_sql_optimistic_lock(request,response);}});thread.start();}return response;}
}

2.Service层,每个请求进来就去数据库里查剩余的库存量,并且抢购成功后,就减1个库存:

package com.mybatis.service.Impl;import com.mybatis.dao.MiaoShaGoodsDao;
import com.mybatis.domain.BaseResponse;
import com.mybatis.domain.MiaoshaRequest;
import com.mybatis.model.MiaoShaGoods;
import com.mybatis.service.MiaoshaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;@Service
public class MiashaServiceImpl implements MiaoshaService{@AutowiredMiaoShaGoodsDao miaoShaGoodsDao;private Lock lock = new ReentrantLock();@Autowiredprivate RedisTemplate<String,String> redisTemplate;/*** 不做任何处理的秒杀实现* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods(MiaoshaRequest request, BaseResponse response) {int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");}else{System.out.println("抢到iphoneX,失败!");}return response;}/*** 数据库乐观锁实现秒杀* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods_sql_optimistic_lock(MiaoshaRequest request,BaseResponse response) {int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_lgs(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods_lgs(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");//做出相应的逻辑(记录抢购成功的用户名什么的....)}else{System.out.println("抢到iphoneX,失败!");//重试或者返回友好的提示什么的....}return response;}
}

3.dao层(mybatis的xml文件)[在SQL层面改为数据库乐观锁]:

<select id="getGoods_lgs" parameterType="java.lang.String" resultMap="BaseResultMap">select<include refid="Base_Column_List" />from miao_sha_goodswhere goods_name = #{goodsName,jdbcType=VARCHAR}</select><update id="updateMsGoods_lgs" parameterType="com.mybatis.model.MiaoShaGoods">update miao_sha_goodsset goods_sum = #{goodsSum,jdbcType=INTEGER},version=version+1where goods_name = #{goodsName,jdbcType=VARCHAR} and version = #{version,jdbcType=VARCHAR}</update>

4.测试结果:

截图表明,总共有500个人抢,有29个人抢购成功,而且库存量减少了29个,这保证了库存的正确性。但却会有抢购不成功的请求,需要我们后续去处理。

三、数据库悲观锁处理的高并发秒杀实现:

1.Controller层,模拟500个并发调用:

package com.mybatis.controller;import com.mybatis.domain.BaseResponse;
import com.mybatis.domain.MiaoshaRequest;
import com.mybatis.service.MiaoshaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;@Controller
@RequestMapping(value="/miaoshagoods")
public class MiaoshaController {@Autowiredpublic MiaoshaService miaoshaService;@PostMapping("/miaosha_java_sql_lock")public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){BaseResponse response=new BaseResponse();for(int i=0;i<500;i++){Thread thread=new Thread(new Runnable() {@Overridepublic void run() {//不做任何处理的秒杀实现//miaoshaService.miaoshaGoods(request,response);//数据库乐观锁秒杀//miaoshaService.miaoshaGoods_sql_optimistic_lock(request,response);//数据库悲观锁秒杀miaoshaService.miaoshaGoods_sql_pessimistic_lock(request,response);}});thread.start();}return response;}
}

2.Service层,每个请求进来就去数据库里查剩余的库存量,并且抢购成功后,就减1个库存(查询和更新必须在同一个事务):

package com.mybatis.service.Impl;import com.mybatis.dao.MiaoShaGoodsDao;
import com.mybatis.domain.BaseResponse;
import com.mybatis.domain.MiaoshaRequest;
import com.mybatis.model.MiaoShaGoods;
import com.mybatis.service.MiaoshaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;@Service
public class MiashaServiceImpl implements MiaoshaService{@AutowiredMiaoShaGoodsDao miaoShaGoodsDao;private Lock lock = new ReentrantLock();@Autowiredprivate RedisTemplate<String,String> redisTemplate;/*** 不做任何处理的秒杀实现* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods(MiaoshaRequest request, BaseResponse response) {int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");}else{System.out.println("抢到iphoneX,失败!");}return response;}/*** 数据库乐观锁实现秒杀* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods_sql_optimistic_lock(MiaoshaRequest request,BaseResponse response) {int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_lgs(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods_lgs(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");//做出相应的逻辑(记录抢购成功的用户名什么的....)}else{System.out.println("抢到iphoneX,失败!");//重试或者返回友好的提示什么的....}return response;}/*** 数据库悲观锁实现秒杀* @param request* @return*/@Override@Transactionalpublic BaseResponse miaoshaGoods_sql_pessimistic_lock(MiaoshaRequest request,BaseResponse response) {int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_bgs(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods_bgs(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");}else{System.out.println("抢到iphoneX,失败!");}return response;}
}

3.dao层(mybatis的xml文件)[在SQL层面改为数据库悲观锁]:

<select id="getGoods_bgs" parameterType="java.lang.String" resultMap="BaseResultMap">select<include refid="Base_Column_List" />from miao_sha_goodswhere goods_name = #{goodsName,jdbcType=VARCHAR} FOR UPDATE</select><update id="updateMsGoods_bgs" parameterType="com.mybatis.model.MiaoShaGoods">update miao_sha_goodsset goods_sum = #{goodsSum,jdbcType=INTEGER}where goods_name = #{goodsName,jdbcType=VARCHAR}</update>

4.测试结果:

截图表明,总共有500个人抢,有100个人抢购成功,而且库存量减少了100个,这保证了库存的正确性。

四、java线程同步锁处理的高并发秒杀实现:

1.Controller层,模拟500个并发调用:

package com.mybatis.controller;import com.mybatis.domain.BaseResponse;
import com.mybatis.domain.MiaoshaRequest;
import com.mybatis.service.MiaoshaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;@Controller
@RequestMapping(value="/miaoshagoods")
public class MiaoshaController {@Autowiredpublic MiaoshaService miaoshaService;@PostMapping("/miaosha_java_sql_lock")public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){BaseResponse response=new BaseResponse();for(int i=0;i<500;i++){Thread thread=new Thread(new Runnable() {@Overridepublic void run() {//不做任何处理的秒杀实现//miaoshaService.miaoshaGoods(request,response);//数据库乐观锁秒杀//miaoshaService.miaoshaGoods_sql_optimistic_lock(request,response);//数据库悲观锁秒杀//miaoshaService.miaoshaGoods_sql_pessimistic_lock(request,response);//java线程同步锁秒杀miaoshaService.miaoshaGoods_java_synchronized_lock(request,response);}});thread.start();}return response;}
}

2.Service层,每个请求进来就去数据库里查剩余的库存量,并且抢购成功后,就减1个库存:

package com.mybatis.service.Impl;import com.mybatis.dao.MiaoShaGoodsDao;
import com.mybatis.domain.BaseResponse;
import com.mybatis.domain.MiaoshaRequest;
import com.mybatis.model.MiaoShaGoods;
import com.mybatis.service.MiaoshaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;@Service
public class MiashaServiceImpl implements MiaoshaService{@AutowiredMiaoShaGoodsDao miaoShaGoodsDao;private Lock lock = new ReentrantLock();@Autowiredprivate RedisTemplate<String,String> redisTemplate;/*** 不做任何处理的秒杀实现* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods(MiaoshaRequest request, BaseResponse response) {int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");}else{System.out.println("抢到iphoneX,失败!");}return response;}/*** 数据库乐观锁实现秒杀* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods_sql_optimistic_lock(MiaoshaRequest request,BaseResponse response) {int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_lgs(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods_lgs(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");//做出相应的逻辑(记录抢购成功的用户名什么的....)}else{System.out.println("抢到iphoneX,失败!");//重试或者返回友好的提示什么的....}return response;}/*** 数据库悲观锁实现秒杀* @param request* @return*/@Override@Transactionalpublic BaseResponse miaoshaGoods_sql_pessimistic_lock(MiaoshaRequest request,BaseResponse response) {int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_bgs(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods_bgs(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");}else{System.out.println("抢到iphoneX,失败!");}return response;}/*** java同步锁实现秒杀* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods_java_synchronized_lock(MiaoshaRequest request,BaseResponse response) {synchronized(this){int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");}else{System.out.println("抢到iphoneX,失败!");}}return response;}
}

3.dao层(mybatis的xml文件):

<select id="getGoods" parameterType="java.lang.String" resultMap="BaseResultMap">select<include refid="Base_Column_List" />from miao_sha_goodswhere goods_name = #{goodsName,jdbcType=VARCHAR}</select><update id="updateMsGoods" parameterType="com.mybatis.model.MiaoShaGoods">update miao_sha_goodsset goods_sum = #{goodsSum,jdbcType=INTEGER}where goods_name = #{goodsName,jdbcType=VARCHAR}</update>

4.测试结果:

截图表明,总共有500个人抢,有100个人抢购成功,而且库存量减少了100个,这保证了库存的正确性。

五、java线程可重入锁处理的高并发秒杀实现:

1.Controller层,模拟500个并发调用:

package com.mybatis.controller;import com.mybatis.domain.BaseResponse;
import com.mybatis.domain.MiaoshaRequest;
import com.mybatis.service.MiaoshaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;@Controller
@RequestMapping(value="/miaoshagoods")
public class MiaoshaController {@Autowiredpublic MiaoshaService miaoshaService;@PostMapping("/miaosha_java_sql_lock")public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){BaseResponse response=new BaseResponse();for(int i=0;i<500;i++){Thread thread=new Thread(new Runnable() {@Overridepublic void run() {//不做任何处理的秒杀实现//miaoshaService.miaoshaGoods(request,response);//数据库乐观锁秒杀//miaoshaService.miaoshaGoods_sql_optimistic_lock(request,response);//数据库悲观锁秒杀//miaoshaService.miaoshaGoods_sql_pessimistic_lock(request,response);//java线程同步锁秒杀//miaoshaService.miaoshaGoods_java_synchronized_lock(request,response);//java线程可重入锁秒杀miaoshaService.miaoshaGoods_java_reentrant_lock(request,response);}});thread.start();}return response;}
}

2.Service层,每个请求进来就去数据库里查剩余的库存量,并且抢购成功后,就减1个库存:

package com.mybatis.service.Impl;import com.mybatis.dao.MiaoShaGoodsDao;
import com.mybatis.domain.BaseResponse;
import com.mybatis.domain.MiaoshaRequest;
import com.mybatis.model.MiaoShaGoods;
import com.mybatis.service.MiaoshaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;@Service
public class MiashaServiceImpl implements MiaoshaService{@AutowiredMiaoShaGoodsDao miaoShaGoodsDao;private Lock lock = new ReentrantLock();@Autowiredprivate RedisTemplate<String,String> redisTemplate;/*** 不做任何处理的秒杀实现* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods(MiaoshaRequest request, BaseResponse response) {int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");}else{System.out.println("抢到iphoneX,失败!");}return response;}/*** 数据库乐观锁实现秒杀* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods_sql_optimistic_lock(MiaoshaRequest request,BaseResponse response) {int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_lgs(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods_lgs(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");//做出相应的逻辑(记录抢购成功的用户名什么的....)}else{System.out.println("抢到iphoneX,失败!");//重试或者返回友好的提示什么的....}return response;}/*** 数据库悲观锁实现秒杀* @param request* @return*/@Override@Transactionalpublic BaseResponse miaoshaGoods_sql_pessimistic_lock(MiaoshaRequest request,BaseResponse response) {int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_bgs(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods_bgs(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");}else{System.out.println("抢到iphoneX,失败!");}return response;}/*** java同步锁实现秒杀* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods_java_synchronized_lock(MiaoshaRequest request,BaseResponse response) {synchronized(this){int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");}else{System.out.println("抢到iphoneX,失败!");}}return response;}/*** java可重入锁实现秒杀* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods_java_reentrant_lock(MiaoshaRequest request,BaseResponse response) {lock.lock();int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);}if(countSuc==1){System.out.println(request.getGoodNames()+"抢到iphoneX,成功!");}else{System.out.println("抢到iphoneX,失败!");}lock.unlock();return response;}
}

3.dao层(mybatis的xml文件):

<select id="getGoods" parameterType="java.lang.String" resultMap="BaseResultMap">select<include refid="Base_Column_List" />from miao_sha_goodswhere goods_name = #{goodsName,jdbcType=VARCHAR}</select><update id="updateMsGoods" parameterType="com.mybatis.model.MiaoShaGoods">update miao_sha_goodsset goods_sum = #{goodsSum,jdbcType=INTEGER}where goods_name = #{goodsName,jdbcType=VARCHAR}</update>

4.测试结果:

截图表明,总共有500个人抢,有100个人抢购成功,而且库存量减少了100个,这保证了库存的正确性。

六、redis单线程处理的高并发秒杀实现(推荐):

1.Controller层,模拟500个并发调用:

package com.mybatis.controller;import com.mybatis.domain.BaseResponse;
import com.mybatis.domain.MiaoshaRequest;
import com.mybatis.service.MiaoshaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;@Controller
@RequestMapping(value="/miaoshagoods")
public class MiaoshaController {@Autowiredpublic MiaoshaService miaoshaService;@Autowiredprivate RedisTemplate<String,String> redisTemplate;@PostMapping("/miaosha_java_sql_lock")public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){BaseResponse response=new BaseResponse();for(int i=0;i<500;i++){Thread thread=new Thread(new Runnable() {@Overridepublic void run() {//不做任何处理的秒杀实现//miaoshaService.miaoshaGoods(request,response);//数据库乐观锁秒杀//miaoshaService.miaoshaGoods_sql_optimistic_lock(request,response);//数据库悲观锁秒杀//miaoshaService.miaoshaGoods_sql_pessimistic_lock(request,response);//java线程同步锁秒杀//miaoshaService.miaoshaGoods_java_synchronized_lock(request,response);//java线程可重入锁秒杀miaoshaService.miaoshaGoods_java_reentrant_lock(request,response);}});thread.start();}return response;}@PostMapping("/miaosha_redis_lock")public @ResponseBody BaseResponse miaoshaRedisLock(@RequestBody MiaoshaRequest request){BaseResponse response=new BaseResponse();//初始化商品数量Integer goodsSum=miaoshaService.getGoodsSum(request);redisTemplate.opsForValue().set(request.getGoodNames()+":goodsSum",goodsSum+"");System.out.println("总共的库存量:"+goodsSum);for(int i=0;i<500;i++){Thread thread=new Thread(new Runnable() {@Overridepublic void run() {miaoshaService.miaoshaGoods_redis(request,response);}});thread.start();}return response;}
}

2.Service层,先把参与秒杀的总库存量写入redis里,然后再利用redis的增量方法去递减:

package com.mybatis.service.Impl;import com.mybatis.dao.MiaoShaGoodsDao;
import com.mybatis.domain.BaseResponse;
import com.mybatis.domain.MiaoshaRequest;
import com.mybatis.model.MiaoShaGoods;
import com.mybatis.service.MiaoshaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;@Service
public class MiashaServiceImpl implements MiaoshaService{@AutowiredMiaoShaGoodsDao miaoShaGoodsDao;private Lock lock = new ReentrantLock();@Autowiredprivate RedisTemplate<String,String> redisTemplate;/*** 不做任何处理的秒杀实现* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods(MiaoshaRequest request, BaseResponse response) {int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");}else{System.out.println("抢到iphoneX,失败!");}return response;}/*** 数据库乐观锁实现秒杀* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods_sql_optimistic_lock(MiaoshaRequest request,BaseResponse response) {int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_lgs(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods_lgs(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");//做出相应的逻辑(记录抢购成功的用户名什么的....)}else{System.out.println("抢到iphoneX,失败!");//重试或者返回友好的提示什么的....}return response;}/*** 数据库悲观锁实现秒杀* @param request* @return*/@Override@Transactionalpublic BaseResponse miaoshaGoods_sql_pessimistic_lock(MiaoshaRequest request,BaseResponse response) {int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_bgs(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods_bgs(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");}else{System.out.println("抢到iphoneX,失败!");}return response;}/*** java同步锁实现秒杀* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods_java_synchronized_lock(MiaoshaRequest request,BaseResponse response) {synchronized(this){int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);}if(countSuc==1){System.out.println("抢到iphoneX,成功!");}else{System.out.println("抢到iphoneX,失败!");}}return response;}/*** java可重入锁实现秒杀* @param request* @return*/@Overridepublic BaseResponse miaoshaGoods_java_reentrant_lock(MiaoshaRequest request,BaseResponse response) {lock.lock();int countSuc=0;MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());if(miaoShaGoods.getGoodsSum()>0){miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);}if(countSuc==1){System.out.println(request.getGoodNames()+"抢到iphoneX,成功!");}else{System.out.println("抢到iphoneX,失败!");}lock.unlock();return response;}@Overridepublic Integer getGoodsSum(MiaoshaRequest request) {MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());return miaoShaGoods.getGoodsSum();}/*** redis实现秒杀* @param response* @return*/@Overridepublic BaseResponse miaoshaGoods_redis(MiaoshaRequest request,BaseResponse response) {//增量计算剩余库存(利用redis的单线程特性)double goodsSurplusSum=redisTemplate.opsForValue().increment(request.getGoodNames()+":goodsSum",-1);if(goodsSurplusSum>=0){System.out.println("抢到iphoneX,成功!还剩下:"+goodsSurplusSum);}else {System.out.println("抢到iphoneX,失败!");}return response;}
}

3.测试结果:

截图表明,总共有500个人抢,有100个人抢购成功,而且库存量减少了100个,这保证了库存的正确性,抢后redis显示是-400个,说明有500个人进来了,有400个人抢不到。

综上所述,要控制库存量,一般要用锁机制。但是一般加锁的话会比较影响性能(只能用于单服务),推荐大家使用redis自带的线程安全属性来实现(可实现分布式锁)。

java高并发秒杀活动的各种简单实现相关推荐

  1. SpringBoot实现Java高并发秒杀系统之Service层开发(二)

    继上一篇文章:SpringBoot实现Java高并发秒杀系统之DAO层开发 我们创建了SpringBoot项目并熟悉了秒杀系统的表设计,下面我们将讲解一下秒杀系统的核心部分:Service业务层的开发 ...

  2. Java高并发秒杀API(三)之Web层

    Java高并发秒杀API(三)之Web层 1. 设计前的分析 Web层内容相关 前端交互设计 Restful规范 SpringMVC Bootstrap + jQuery 前端页面流程 详情页流程逻辑 ...

  3. SpringBoot、Redis轻松实现Java高并发秒杀系统笔记

    秒杀项目 优极限[完整项目实战]半天带你用SpringBoot.Redis轻松实现Java高并发秒杀系统 文章目录 秒杀项目 技术栈 课程介绍 学习目标 如何设计一个秒杀系统 项目搭建 分布式会话 登 ...

  4. SpringBoot实现Java高并发秒杀系统之DAO层开发(一)

    SpringBoot实现Java高并发秒杀系统之DAO层开发(一) 秒杀系统在如今电商项目中是很常见的,最近在学习电商项目时讲到了秒杀系统的实现,于是打算使用SpringBoot框架学习一下秒杀系统( ...

  5. Java高并发秒杀API(四)之高并发优化

    Java高并发秒杀API(四)之高并发优化 1. 高并发优化分析 关于并发 并发性上不去是因为当多个线程同时访问一行数据时,产生了事务,因此产生写锁,每当一个获取了事务的线程把锁释放,另一个排队线程才 ...

  6. Java高并发秒杀系统【观后总结】

    项目简介 在慕课网上发现了一个JavaWeb项目,内容讲的是高并发秒杀,觉得挺有意思的,就进去学习了一番. 记录在该项目 我结合其资料和观看视频的时候整理出从该项目学到了什么... 项目Dao层 日志 ...

  7. Java高并发秒杀平台(Redis + RabbitMQ)

    Seconds-Kill 本项目是一个模拟高并发环境下基于 SpringBoot 的秒杀购物平台.为了减少对数据库的直接访问,通过 Redis 实现了缓存优化:并通过 RabbitMQ 消息中间件来接 ...

  8. Java高并发秒杀Api-业务分析与DAO层构建1

    章节目录 1.为什么使用Spring+Spring MVC+Mybatis 2.秒杀业务特性 3.秒杀分析过程.优化思路 4.相关技术介绍 5.基于Maven创建项目 6.秒杀业务分析 7.秒杀事务的 ...

  9. Java高并发秒杀高并发优化

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 1.秒杀优化原因: (1)无法使用CDN缓存,其只针对核心数据做缓存 (2)在后端库存操作中,不能在缓存 ...

最新文章

  1. 腾讯开放TAPD、持续集成平台等核心研发工具,加速AI落地
  2. java.io.IOException No FileSystem for scheme hdfs
  3. vb.net 文本框为空提示,一键清空文本框所有内容
  4. java 最快平衡几个值_Java 集合框架面试问题集锦
  5. chrome 发送请求出现:Provisional headers are shown 提示
  6. sublime text多文件夹查找关键字
  7. 为Linux发行版安装simsun.ttf
  8. PL读写PS端DDR的设计
  9. Java概述、Jdk的安装、关键字
  10. 优雅编程之项目注意这些,你就“正常”了(十七)
  11. 网络错误0x80070005,访问被拒绝[亲测解决]
  12. 前端js实现批量下载文件
  13. 周报,当前是第几周 ?
  14. 【TypeError: Descriptors cannot not be created directly. 】解决方法
  15. oracle 从执行计划的预估行数看执行计划是否正确
  16. 最简单DIY基于ESP8266的智能彩灯②(在网页用按键和滑动条控制RGB灯)
  17. 如何使用FFmpeg的解码器
  18. android最新源码(4.4.2_r1版本以上)下载
  19. 专业计算机能力考试 技巧,计算机二级考试复习技巧
  20. java递归20元买汽水,瓶盖递归 1块钱买一瓶水,三个瓶盖是换一瓶汽水,问20块钱能买 多少瓶汽水(不能借)...

热门文章

  1. Java语言项目作业:部门员工管理
  2. 探索工业互联网领域中的设备通信协议
  3. 关于Windows无法访问指定设备路径或文件
  4. 金蝶K3物料取消序列号管理
  5. 一款生成gif图片的工具
  6. OCR应用:银行卡识别
  7. [課程筆記] 機器學習基石 - W7. The VC Dimension
  8. 实体类或对象序列化时,忽略为空属性
  9. 长期低头看书可能会导致颈椎病
  10. mysql全文索引的搜索模式,MySQL5.7官方文档翻译: 全文索引,自然语言搜索模式