一、引入相關依賴
可以新建Spring或Maven工程,在pom文件中引入Jedis依賴:
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
二、核心代碼
SecKillDemo
package com.redis;
import java.util.List;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
public class SecKillDemo implements Runnable {
//這裏需要更改成自己的Redis服務器地址
private Jedis jedis = new Jedis("10.128.*.*", 6379);
//顧客
private String customerName;
//商品
private String key;
public SecKillDemo1(String customerName, String key) {
this.customerName = customerName;
this.key = key;
}
@Override
public void run() {
boolean success = false;
String data;
int currentNum;
while (!success) {
//可重複搶購直到成功
//通過watch實現redis的incr(原子遞增操作)
jedis.watch(key);
data = jedis.get(key);
//獲取剩餘商品數
currentNum = Integer.parseInt(data);
if (currentNum > 0) {
//開啓事務
Transaction transaction = jedis.multi();
//設置新值,如果key的值被其它連接的客戶端修改,那麼當前連接的exec命令將執行失敗
//方式一 ,在事務中設置data的值爲原值減1,此時transaction.exec()的返回值的第一個元素是"OK"
currentNum--;
transaction.set(key, String.valueOf(currentNum));
//方式二,在事務中對鍵data對應的值做減1操作,此時transaction.exec()的返回值的第一個元素是data對應的當前值
//transaction.decrBy(key, Integer.valueOf(currentNum--));
List res = transaction.exec();
if (res.size() == 0) {
String failUserInfo = "fail---" + customerName;
String failMsg = failUserInfo + ",搶購失敗,剩餘商品數量:"+ currentNum ;
// 將秒殺失敗的用戶信息存入Redis。
jedis.setnx(failUserInfo, failMsg);
System.out.println(customerName + " 搶購失敗");
} else {
success = true;
System.out.println(customerName + " 搶購成功,[" + key + "]剩餘:" + currentNum);
String succUserInfo = customerName + " 搶購成功,[" + key + "]";
String succMsg = succUserInfo + ",搶購成功," + "剩餘商品數量:"+currentNum;
System.out.println(res.get(0)+ " " + succMsg);
// 將秒殺成功的用戶信息存入Redis。
jedis.setnx(succUserInfo, succMsg);
}
} else {
System.out.println("商品售空,活動結束!");
System.exit(0);
}
}
}
}
三、單元測試
SecKillDemoTest
package com.redis;
import java.util.Collections;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.Before;
import org.junit.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import redis.clients.jedis.Jedis;
public class SecKillDemoTest {
//商品名稱
private static String key = "華爲P40";
//商品數量
private static String num = "50";
// 模擬用戶搶購最大併發數
private static ExecutorService executorService = Executors.newFixedThreadPool(5);
@Before
public void before() {
Jedis jedis = new Jedis("10.128.*.*");
//命令包括set、get、del等
//eval代表執行Lua語言的命令
//KEYS[1]代表傳遞給Lua腳本的第一個key參數,
//ARGV[1]代表第一個非key參數
String script = "redis.call('del',KEYS[1]);return redis.call('set',KEYS[1],ARGV[1])";
jedis.eval(script, Collections.singletonList(key), Collections.singletonList(num));
jedis.close();
}
@Test
public void test() {
}
public static void main(String[] args) {
try{
for (int i = 1; i <= 100; i++) {
executorService.submit(new SecKillDemo("顧客"+i,key));
}
}catch (Exception e) {
e.printStackTrace();
}finally {
executorService.shutdown();
}
}
}
執行步驟:
(1)執行junit單元測試方法,在redis中構建<key,value>數據。
(2)執行SecKillDemoTest 中的main方法。
執行結果如下:
從redis中查看結果如下:
參考文章:
使用Redis中間件解決商品秒殺活動中出現的超賣問題(使用Java多線程模擬高併發環境)
redis watch命令實現秒殺
Redis常用技術-----使用Lua語言