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