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

众所周知,电商系统的秒杀活动是高并发的很好应用场景,这里用的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. 重温强化学习之策略梯度算法
  2. 01 | 基础架构:一条 SQL 查询语句是如何执行的
  3. linux - python
  4. matlab veristand,amesim Veristand matlab
  5. C#基础温习(10):C#实现托盘功能
  6. javascript学习心得(1)replace
  7. linux was8.5 无法启动wct.sh,WebSphere(was8.5) 在linux集群安装及部署.docx
  8. 《Java EE互联网轻量级框架整合开发》在京东预售,发个目录
  9. 基于文本数据的情感分析系统
  10. Windows2008 R2配置FTP教程
  11. 下载并安装 J2SDK以及运行第一个java程序
  12. 基于java+jsp房屋租赁管理系统
  13. 在VR里弹空气吉他?开发者:手势优化有技巧
  14. springboot智慧餐厅点餐餐桌预订系统设计与实现
  15. html显示隐藏图片,简单的图片显示与隐藏
  16. Revit导入CAD图纸,要提前优化图纸,你做到了吗?
  17. PDPS软件:导出AutoCAD可编辑的2D布局图
  18. 2015 数学建模 国赛(高教杯)-B题 “互联网+”时代的出租车资源配置
  19. 【洛谷 P6151 [集训队作业2019] 青春猪头少年不会梦到兔女郎学姐】【容斥原理+生成函数】
  20. Perl错误处理机制

热门文章

  1. iOS根据屏幕的宽高等比压缩图片
  2. 通过JAVA编写一个简单的雇员管理系统小项目
  3. Android 从app中调起小程序(必成)!!!
  4. m_map投影_引用matlab关于m_map运用
  5. 使用Dreamweaver/利用HTML5/CSS/制作一个简单的文字logo
  6. 中文及英文词云的制作
  7. 学校微机室计算机清单模板,小学微机室资产清单.doc
  8. 48 webpack优化-打包可视化
  9. 获取地址栏倒数第一个“/”和倒数第二个之间“/”的数值
  10. 数据结构——HashMap(内含图形演示)