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();</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> 1000线程抢购100个商品 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> ExecutorService executorService </span>= Executors.newFixedThreadPool(20<span style="color: rgba(0, 0, 0, 1)">); CountDownLatch count </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> CountDownLatch(10000<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">long</span> startTime =<span style="color: rgba(0, 0, 0, 1)"> System.currentTimeMillis(); </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i < 10000; i++<span style="color: rgba(0, 0, 0, 1)">) { executorService.execute(</span><span style="color: rgba(0, 0, 255, 1)">new</span> SecondKillHandlder("user" +<span style="color: rgba(0, 0, 0, 1)"> i)); count.countDown(); } executorService.shutdown(); count.await(); </span><span style="color: rgba(0, 0, 255, 1)">long</span> time = System.currentTimeMillis() -<span style="color: rgba(0, 0, 0, 1)"> startTime; System.out.println(</span>"共耗时:" + time + "毫秒"<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> JedisPoolUtil.close();</span>
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;</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> SecondKillHandlder(String userName) { </span><span style="color: rgba(0, 0, 255, 1)">this</span>.userName =<span style="color: rgba(0, 0, 0, 1)"> userName; } @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> run() { </span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">) { </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { jedis </span>=<span style="color: rgba(0, 0, 0, 1)"> JedisPoolUtil.getJedis(); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> watch 监视一个key,当事务执行之前这个key发生了改变,事务会被打断</span>
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;</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> createJedisPool() { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 建立连接池配置参数</span> JedisPoolConfig config = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JedisPoolConfig(); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置最大连接数</span> config.setMaxTotal(100<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置最大阻塞时间,记住是毫秒数milliseconds</span> config.setMaxWaitMillis(1000<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置空间连接</span> config.setMaxIdle(10<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建连接池</span> pool = <span style="color: rgba(0, 0, 255, 1)">new</span> JedisPool(config, "192.168.31.201", 6379, 2000, <span style="color: rgba(0, 0, 255, 1)">null</span>, 3<span style="color: rgba(0, 0, 0, 1)">); } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> 在多线程环境同步初始化 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">synchronized</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> poolInit() { </span><span style="color: rgba(0, 0, 255, 1)">if</span> (pool == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) createJedisPool(); } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 获取一个jedis 对象 * * </span><span style="color: rgba(128, 128, 128, 1)">@return</span> <span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> Jedis getJedis() { </span><span style="color: rgba(0, 0, 255, 1)">if</span> (pool == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) poolInit(); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> pool.getResource(); } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 归还一个连接 * * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> jedis </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> returnRes(Jedis jedis) { pool.returnResource(jedis); } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> close() { pool.close(); }
}
}