1.redis版本:2.9.0
2.第一種實現方案:具體實現方法如下:
public class RedisTool {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
/**
* 嘗試獲取分佈式鎖
* @param jedis Redis客戶端
* @param lockKey 鎖
* @param requestId 請求標識(可以用UUID代替)
* @param expireTime 超期時間
* @return 是否獲取成功
*/
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
private static final Long RELEASE_SUCCESS = 1L;
/**
* 釋放分佈式鎖
* @param jedis Redis客戶端
* @param lockKey 鎖
* @param requestId 請求標識
* @return 是否釋放成功
*/
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
}
3.第二種實現方案:Redis的watch命令(配合multi、exec使用,保證redis事務)
會話1 利用watch監控key,multi開啓事務之後,在 會話1 使用exec執行提交事務之前,如果key中途被 會話2 修改值,最後 會話1 在exec提交自己修改的值時會執行失敗。該機制可以保證,在一個事務裏,有且僅有一個操作提交成功。此解決方案,非常適合如 商品秒殺,出價競拍等。
示例代碼如下:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import java.util.List;
public class RedisWatchTest extends Thread {
private String auctionCode;
public RedisWatchTest
(String auctionCode) {
super(auctionCode);
this.auctionCode = auctionCode;
}
private static int bidPrice = 100;
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + "主線程運行開始!");
//更改key爲a的值
Jedis jedis=new Jedis("127.0.0.1",6379);
jedis.set("goodsprice","0");
System.out.println("輸出初始化值:"+jedis.get("goodsprice"));
jedis.close();
RedisWatchTest thread1 = new RedisWatchTest("A001");
RedisWatchTest thread2 = new RedisWatchTest("B001");
thread1.start();
thread2.start();
try{
thread1.join();
thread2.join();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "主線程運行結束!");
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "線程運行開始 ");
Jedis jedis=new Jedis("127.0.0.1",6379);
try {
if(Thread.currentThread().getName()=="B001"){
sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
//監視KEY
jedis.watch("goodsprice");
//A先進
String v = jedis.get("goodsprice");
Integer iv = Integer.valueOf(v);
//條件都給過
if(bidPrice > iv){
Transaction tx = jedis.multi();// 開啓事務
Integer bp = iv + 100;
//出價成功,事務未提交
tx.set("goodsprice",String.valueOf(bp));
System.out.println("子線程" + auctionCode + "set成功");
try {
if(Thread.currentThread().getName()=="A001"){
sleep(2000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
List<Object> list = tx.exec();
if (list == null ||list.size()==0) {
System.out.println("子線程" + auctionCode + ",出價失敗");
}else{
System.out.println("子線程"+this.auctionCode +", 出價:"+ jedis.get("goodsprice") +",出價時間:"
+ System.nanoTime());
}
}else{
System.out.println("出價低於現有價格!");
}
jedis.close();
System.out.println(Thread.currentThread().getName() + "線程運行結束");
}
}