【轉載】Redis分佈式鎖的實現

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() + "線程運行結束");
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章