首頁 > Java > java教程 > 主體

利用Redis實現分散式鎖的詳解(程式碼範例)

不言
發布: 2018-10-20 17:00:28
轉載
3413 人瀏覽過

這篇文章帶給大家的內容是關於利用Redis實現分散式鎖的詳解(程式碼範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

選用Redis實現分散式鎖定原因

Redis有很高的效能

Redis指令對此支援較好,實作起來比較方便

使用指令介紹

SETNX

SETNX key val
當且僅當key不存在時,set一個key為val的字串,傳回1 ;若key存在,則什麼都不做,回傳0。

expire

expire key timeout
為key設定一個逾時時間,單位為second,超過這個時間鎖定會自動釋放,避免死鎖。

delete

delete key
刪除key

在使用Redis實現分散式鎖定的時候,主要就會使用到這三個指令。

實作

使用的是jedis來連接Redis。

實作想法

取得鎖的時候,使用setnx加鎖,並使用expire指令為鎖添加一個超時時間,超過該時間則自動釋放鎖,鎖的value值為一個隨機產生的UUID,透過此在釋放鎖的時候進行判斷。

取得鎖定的時候也設定一個取得的逾時時間,若超過這個時間就放棄取得鎖定。

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

分散式鎖定的核心程式碼如下:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisException;
 
import java.util.List;
import java.util.UUID;
 
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()  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;
    }
}
登入後複製

測試

下面就用一個簡單的範例測試剛才實作的分散式鎖定。
例子中使用50個執行緒模擬秒殺一個商品,使用--運算子來實現商品減少,從結果有序性就可以看出是否為加鎖狀態。

模擬秒殺服務,在其中配置了jedis執行緒池,在初始化的時候傳給分散式鎖,供其使用。

import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class Service {
    private static JedisPool pool = null;
 
    static {
        JedisPoolConfig config = new JedisPoolConfig();
        // 设置最大连接数
        config.setMaxTotal(200);
        // 设置最大空闲数
        config.setMaxIdle(8);
        // 设置最大等待时间
        config.setMaxWaitMillis(1000 * 100);
        // 在borrow一个jedis实例时,是否需要验证,若为true,则所有jedis实例均是可用的
        config.setTestOnBorrow(true);
        pool = new JedisPool(config, "127.0.0.1", 6379, 3000);
    }
 
    DistributedLock lock = new DistributedLock(pool);
 
    int n = 500;
 
    public void seckill() {
        // 返回锁的value值,供释放锁时候进行判断
        String indentifier = lock.lockWithTimeout("resource", 5000, 1000);
        System.out.println(Thread.currentThread().getName() + "获得了锁");
        System.out.println(--n);
        lock.releaseLock("resource", indentifier);
    }
}
登入後複製

模擬執行緒進行秒殺服務

public class ThreadA extends Thread {
    private Service service;
 
    public ThreadA(Service service) {
        this.service = service;
    }
 
    @Override
    public void run() {
        service.seckill();
    }
}
 
public class Test {
    public static void main(String[] args) {
        Service service = new Service();
        for (int i = 0; i <p>結果如下,結果為有序的。 </p><p style="text-align: center;"><span class="img-wrap"><img src="https://img.php.cn//upload/image/211/984/157/1540025904941327.png" title="1540025904941327.png" alt="利用Redis實現分散式鎖的詳解(程式碼範例)"></span></p><p>若註解掉使用鎖定的部分</p><pre class="brush:php;toolbar:false">public void seckill() {
    // 返回锁的value值,供释放锁时候进行判断
    //String indentifier = lock.lockWithTimeout("resource", 5000, 1000);
    System.out.println(Thread.currentThread().getName() + "获得了锁");
    System.out.println(--n);
    //lock.releaseLock("resource", indentifier);
}
登入後複製

從結果可以看出,有些是非同步進行的。

利用Redis實現分散式鎖的詳解(程式碼範例)

在分散式環境中,對資源上鎖有時候是很重要的,例如搶購某一資源,這時候使用分散式鎖就可以很好地控制資源。

當然,在具體使用中,還需要考慮許多因素,例如逾時時間的選取,而取得鎖定時間的選取對並發量都有很大的影響,上述實現的分散式鎖定也只是一種簡單的實現,主要是一種思想。

[其實,使用zookeeper的可靠性是要大於使用redis實現的分散式鎖定的,但是相比而言,redis的效能更好。 ]

以上是利用Redis實現分散式鎖的詳解(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:segmentfault.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!