基於Redis實現分佈式鎖

之前項目中使用redis鎖實現秒殺等一些併發業務,在這裏整理一下基於Redis實現分佈式鎖的簡單入門實例,記錄一下,便於以後查看、學習。

參考博客:

https://blog.csdn.net/l_bestcoder/article/details/79336986

https://blog.csdn.net/starlh35/article/details/79759630

1、簡介

在分佈式系統中存在併發場景,爲了解決這一問題,基於redis鎖一定程度可以解決這一問題,但是也有缺點,如死鎖等。。。,

這塊的優缺點,大家自行百度學習一下。。。這裏主要介紹的基於redis實現鎖的機制!

分佈式系統實現的時候要注意的幾個關鍵點:

1、鎖信息必須是會過期超時的,不能讓一個線程長期佔有一個鎖而導致死鎖;

2、同一時刻只能有一個線程獲取到鎖。

主要實現鎖功能的redis命令:

setnx(key, value):“set if not exits”,若該key-value不存在,則成功加入緩存並且返回1,否則返回0。

get(key):獲得key對應的value值,若不存在則返回nil。

getset(key, value):先獲取key對應的value值,若不存在則返回nil,然後將舊的value更新爲新的value。

expire(key, seconds):設置key-value的有效期爲seconds秒,避免死鎖

delete (key):刪除key

2、實現思路

a、加鎖

當執行一個線程業務時,通過加鎖(獲取鎖)實現防止別的線程去執行它;

獲取鎖的時候,使用setnx加鎖,並使用expire命令爲鎖添加一個超時時間,超過該時間則自動釋放鎖,鎖的value值爲一個隨機生成的UUID,通過此在釋放鎖的時候進行判斷。
獲取鎖的時候還設置一個獲取的超時時間,若超過這個時間則放棄獲取鎖。


b、釋放鎖

釋放鎖的時候,通過UUID判斷是不是該鎖,若是該鎖,則執行delete進行鎖釋放。

3、代碼實現

其實代碼實現大同小異,實現思想就是上面的思路。。。

/**
 * 實現 redis 加鎖、釋放鎖
 */
public class DistributedLock 
{
    private final JedisPool jedisPool;

    public DistributedLock(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    /**
     * 加鎖
     * 
     * @param locaName
     *            鎖的key
     * @param acquireTimeout
     *            獲取超時時間
     * @param timeout
     *            鎖的超時時間
     * @return 鎖標識
     */
    public String lockWithTimeout(String locaName, long acquireTimeout, long timeout) {
        Jedis conn = null;
        String retIdentifier = null;
        try {
            // 獲取連接
            conn = jedisPool.getResource();
            // 隨機生成一個value
            String identifier = UUID.randomUUID().toString();
            // 鎖名,即key值
            String lockKey = "lock:" + locaName;
            // 超時時間,上鎖後超過此時間則自動釋放鎖
            int lockExpire = (int) (timeout / 1000);

            // 獲取鎖的超時時間,超過這個時間則放棄獲取鎖
            long end = System.currentTimeMillis() + acquireTimeout;
            while (System.currentTimeMillis() < end) {
                if (conn.setnx(lockKey, identifier) == 1) {
                    conn.expire(lockKey, lockExpire);
                    // 返回value值,用於釋放鎖時間確認
                    retIdentifier = identifier;
                    return retIdentifier;
                }
                // 返回-1代表key沒有設置超時時間,爲key設置一個超時時間
                if (conn.ttl(lockKey) == -1) {
                    conn.expire(lockKey, lockExpire);
                }

                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
        return retIdentifier;
    }

    /**
     * 釋放鎖
     * 
     * @param lockName
     *            鎖的key
     * @param identifier
     *            釋放鎖的標識
     * @return
     */
    public boolean releaseLock(String lockName, String identifier) {
        Jedis conn = null;
        String lockKey = "lock:" + lockName;
        boolean retFlag = false;
        try {
            conn = jedisPool.getResource();
            while (true) {
                // 監視lock,準備開始事務
                conn.watch(lockKey);
                // 通過前面返回的value值判斷是不是該鎖,若是該鎖,則刪除,釋放鎖
                if (identifier.equals(conn.get(lockKey))) {
                    Transaction transaction = conn.multi();
                    transaction.del(lockKey);
                    List<Object> results = transaction.exec();
                    if (results == null) {
                        continue;
                    }
                    retFlag = true;
                }
                conn.unwatch();
                break;
            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
        return retFlag;
    }
}
4、測試案例

模擬線程併發業務。。。

線程服務

加鎖後運行測試:  20個子線程業務被順序執行

 不加鎖後運行測試:  20個子線程業務被非順序執行,誰搶到資源誰先執行。。

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