java基於redis事務的秒殺實現

package com.vian.user.service;

import org.junit.Test;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Transaction;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/** 秒殺測試 */
public class SecondKillTest {
  @Test
  public void getResult() {
    Jedis jedis = JedisPoolUtil.getJedis();
    String goodsResult = jedis.get("goodsResult:user102");
    System.out.println(goodsResult);
  }

  @Test
  public void test() throws IOException, InterruptedException {
    /** 初始化商品 */
    initGoods();

    /** 1000線程搶購100個商品 */
    ExecutorService executorService = Executors.newFixedThreadPool(20);
    CountDownLatch count = new CountDownLatch(10000);
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < 10000; i++) {
      executorService.execute(new SecondKillHandlder("user" + i));
      count.countDown();
    }
    executorService.shutdown();
    count.await();
    long time = System.currentTimeMillis() - startTime;
    System.out.println("共耗時:" + time + "毫秒");
    // JedisPoolUtil.close();
    System.in.read();
  }

  /** 初始化商品數量 */
  private void initGoods() {
    Jedis jedis = JedisPoolUtil.getJedis();
    jedis.set("goods:iphone8", "100"); // 設置100個商品
    JedisPoolUtil.returnRes(jedis);
  }

  /** 秒殺處理線程 */
  private static class SecondKillHandlder implements Runnable {
    String goodsKey = "goods:iphone8"; // 監視的key 當前秒殺商品的數量
    Jedis jedis;
    String userName;

    public SecondKillHandlder(String userName) {
      this.userName = userName;
    }

    @Override
    public void run() {
      while (true) {
        try {
          jedis = JedisPoolUtil.getJedis();
          // watch 監視一個key,當事務執行以前這個key發生了改變,事務會被打斷
          jedis.watch(goodsKey);
          int currentGoodsCount = Integer.parseInt(jedis.get(goodsKey)); // 當前剩餘商品數量
          if (currentGoodsCount <= 0) {
            System.out.println("商品已搶完," + userName + "---> 搶購失敗 XXX");
            break;
          }
          Transaction tran = jedis.multi(); // 開啓事務
          tran.incrBy(goodsKey, -1); // 商品數量-1
          List<Object> exec = tran.exec(); // 執行事務
          if (CollectionUtils.isEmpty(exec)) {
            System.out.println(userName + "---> 搶購失敗,繼續搶購");
            Thread.sleep(1);
          } else {
            exec.forEach(
                succ -> {
                  String succStr =
                      userName
                          + "===========================> 搶購到第【"
                          + ((100 - currentGoodsCount) + 1)
                          + "】份商品,該商品剩餘:"
                          + succ.toString();
                  System.out.println(succStr);
                  jedis.set("goodsResult:" + userName, succStr); // 業務代碼,處理搶購成功
                });
            break;
          }
        } catch (Exception e) {
          e.printStackTrace();
        } finally {
          if (jedis != null) {
            jedis.unwatch();
            JedisPoolUtil.returnRes(jedis);
          }
        }
      }
    }
  }

  private static class JedisPoolUtil {
    private static JedisPool pool;

    private static void createJedisPool() {
      // 創建鏈接池配置參數
      JedisPoolConfig config = new JedisPoolConfig();
      // 設置最大鏈接數
      config.setMaxTotal(100);
      // 設置最大阻塞時間,記住是毫秒數milliseconds
      config.setMaxWaitMillis(1000);
      // 設置空間鏈接
      config.setMaxIdle(10);
      // 建立鏈接池
      pool = new JedisPool(config, "192.168.31.201", 6379, 2000, null, 3);
    }

    /** 在多線程環境同步初始化 */
    private static synchronized void poolInit() {
      if (pool == null) createJedisPool();
    }

    /**
     * 獲取一個jedis 對象
     *
     * @return
     */
    public static Jedis getJedis() {

      if (pool == null) poolInit();
      return pool.getResource();
    }

    /**
     * 歸還一個鏈接
     *
     * @param jedis
     */
    public static void returnRes(Jedis jedis) {
      pool.returnResource(jedis);
    }

    public static void close() {
      pool.close();
    }
  }
}

 

相關文章
相關標籤/搜索