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();
    }
  }
}

 

 

posted @ 2018-06-25 15:46  艺言弈行  阅读(3707)  评论(0编辑  收藏  举报