java高並發秒殺活動的各種簡單實現【springBoot+mybatis+redis+mysql】


最近遇到比較多數據不一致的問題,大多數都是因為並發請求時,沒及時處理的原因,故用一個比較有代表性的業務場景【活動秒殺】來模擬一下這個這種高並發所產生的問題。

首先搭建一個springboot項目在這里我做演示了,不會的可以自行百度,搭建過程很簡單。

1:搭建好的項目目錄結構

 

2:商品表(記錄商品名稱,本次可以秒殺的庫存量)

 

加了一條記錄(后面每次測試都先手動把庫存恢復成100才進行測試)

 

 

3:實體類(這里不用實體類也可以,根據自己的需求來)

 

一、不做任何處理的高並發秒殺實現(錯誤演示):

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 {
 
    @Autowired
    public 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() {
                    @Override
                    public 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{
 
    @Autowired
    MiaoShaGoodsDao miaoShaGoodsDao;
 
    private Lock lock = new ReentrantLock();
 
    @Autowired
    private RedisTemplate<String,String> redisTemplate;
 
    /**
     * 不做任何處理的秒殺實現
     * @param request
     * @return
     */
    @Override
    public 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_goods
    where goods_name = #{goodsName,jdbcType=VARCHAR}
  </select>
 
  <update id="updateMsGoods" parameterType="com.mybatis.model.MiaoShaGoods">
    update miao_sha_goods
    set goods_sum = #{goodsSum,jdbcType=INTEGER}
    where goods_name = #{goodsName,jdbcType=VARCHAR}
  </update>

 4.測試結果:

 

 

 

 

 

 

 截圖表明,居然有500個人搶購成功,而且庫存量卻只減少了12個,這是明顯是錯誤的。

 

二、數據庫樂觀鎖處理的高並發秒殺實現:

 

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 {
 
    @Autowired
    public 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() {
                @Override
                public 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{
     
        @Autowired
        MiaoShaGoodsDao miaoShaGoodsDao;
     
        private Lock lock = new ReentrantLock();
     
        @Autowired
        private RedisTemplate<String,String> redisTemplate;
     
        /**
         * 不做任何處理的秒殺實現
         * @param request
         * @return
         */
        @Override
        public 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
         */
        @Override
        public 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_goods
        where goods_name = #{goodsName,jdbcType=VARCHAR}
      </select>
     
      <update id="updateMsGoods_lgs" parameterType="com.mybatis.model.MiaoShaGoods">
        update miao_sha_goods
        set goods_sum = #{goodsSum,jdbcType=INTEGER},version=version+1
        where 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 {
     
        @Autowired
        public 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() {
                    @Override
                    public 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{
     
        @Autowired
        MiaoShaGoodsDao miaoShaGoodsDao;
     
        private Lock lock = new ReentrantLock();
     
        @Autowired
        private RedisTemplate<String,String> redisTemplate;
     
        /**
         * 不做任何處理的秒殺實現
         * @param request
         * @return
         */
        @Override
        public 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
         */
        @Override
        public 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
        @Transactional
        public 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_goods
        where goods_name = #{goodsName,jdbcType=VARCHAR} FOR UPDATE
      </select>
     
      <update id="updateMsGoods_bgs" parameterType="com.mybatis.model.MiaoShaGoods">
        update miao_sha_goods
        set 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 {
     
        @Autowired
        public 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() {
                    @Override
                    public 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{
     
        @Autowired
        MiaoShaGoodsDao miaoShaGoodsDao;
     
        private Lock lock = new ReentrantLock();
     
        @Autowired
        private RedisTemplate<String,String> redisTemplate;
     
        /**
         * 不做任何處理的秒殺實現
         * @param request
         * @return
         */
        @Override
        public 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
         */
        @Override
        public 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
        @Transactional
        public 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
         */
        @Override
        public 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_goods
        where goods_name = #{goodsName,jdbcType=VARCHAR}
      </select>
     
      <update id="updateMsGoods" parameterType="com.mybatis.model.MiaoShaGoods">
        update miao_sha_goods
        set 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 {
     
        @Autowired
        public 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() {
                    @Override
                    public 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{
     
        @Autowired
        MiaoShaGoodsDao miaoShaGoodsDao;
     
        private Lock lock = new ReentrantLock();
     
        @Autowired
        private RedisTemplate<String,String> redisTemplate;
     
        /**
         * 不做任何處理的秒殺實現
         * @param request
         * @return
         */
        @Override
        public 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
         */
        @Override
        public 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
        @Transactional
        public 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
         */
        @Override
        public 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
         */
        @Override
        public 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_goods
        where goods_name = #{goodsName,jdbcType=VARCHAR}
      </select>
     
      <update id="updateMsGoods" parameterType="com.mybatis.model.MiaoShaGoods">
        update miao_sha_goods
        set 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 {
     
        @Autowired
        public MiaoshaService miaoshaService;
     
        @Autowired
        private 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() {
                    @Override
                    public 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;
        }
      // redis 防止超賣
       @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() {
                    @Override
                    public 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{
     
        @Autowired
        MiaoShaGoodsDao miaoShaGoodsDao;
     
        private Lock lock = new ReentrantLock();
     
        @Autowired
        private RedisTemplate<String,String> redisTemplate;
     
        /**
         * 不做任何處理的秒殺實現
         * @param request
         * @return
         */
        @Override
        public 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
         */
        @Override
        public 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
        @Transactional
        public 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
         */
        @Override
        public 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
         */
        @Override
        public 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;
        }
     
        @Override
        public Integer getGoodsSum(MiaoshaRequest request) {
            MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());
            return miaoShaGoods.getGoodsSum();
        }
        /**
         * redis實現秒殺
         * @param response
         * @return
         */
        @Override
        public 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自帶的線程安全屬性來實現(可實現分布式鎖)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM