Redis分佈式鎖實現

平常的開發中,咱們常常會遇到不一樣線程或進程須要互斥訪問資源的狀況,這時候分佈式的鎖就是很是有必要的了。分佈式鎖實現方式能夠有不少種:如mysql、redis、zookeeper等。java

這裏簡單地分享一下基於redis的實現。mysql

這裏使用了幾個主要的redis命令: set 、get 、del。redis

SET的命令格式:sql

SET key value NX PX milliseconds
SET key value NX EX seconds

這裏必定要使用NX,由於NX表示只有key不存在時,SET才能成功。dom

從接口上,鎖應該分爲獲取鎖和釋放鎖。分佈式

獲取鎖:經過set命令設置一個key的值,並返回鎖的內容。若是沒有set成功,即表示已經被其它線程或進程佔用,能夠繼續嘗試獲取。ui

釋放鎖:經過del命令將這個key值刪除便可,可是爲保證釋放鎖的可靠性須要驗證鎖的內容。this

下面貼上具體的實現代碼,註釋也比較清楚,再也不詳細描述。線程

package com.zheng.coderepo.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * redis分佈式鎖
 * Created by zhangchaozheng on 16-7-28.
 */
public class RedisLock {

    private final static String UNLOCK_LUA = "" +
            "if redis.call(\"get\",KEYS[1]) == ARGV[1] " +
            "then " +
            "   return redis.call(\"del\",KEYS[1]) " +
            "else " +
            "   return 0 " +
            "end";

    //等待鎖時間
    private final static long WAIT_LOCK_TIME = 300;

    private JedisPool jedisPool;

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

    /**
     * 若是分佈式鎖不可用, 當前線程將繼續請求, 直到鎖可用.
     *
     * @param lockKey 鎖的鍵值
     * @param timeout 超時時間.單位:毫秒.
     * @return 鎖的值.
     */
    public String lock(String lockKey, long timeout) {
        String lockVal = UUID.randomUUID().toString();

        for (; ; ) {
            if (!tryLock(lockKey, lockVal, timeout)) {
                try {
                    TimeUnit.MILLISECONDS.sleep(WAIT_LOCK_TIME);
                } catch (InterruptedException e) {
                }
            } else {
                break;
            }
        }
        return lockVal;
    }

    /**
     * 嘗試得到分佈式鎖, 若是鎖可用, 返回true
     *
     * @param lockKey 鎖的鍵值
     * @param value   鎖的值,推薦使用隨機字符.
     * @param timeout 超時時間.單位:毫秒.
     * @return true/false
     */
    public boolean tryLock(String lockKey, String value, long timeout) {
        Jedis jedis = jedisPool.getResource();
        try {
            Object result = jedis.set(lockKey, value, "NX", "PX", timeout);
            return "OK".equalsIgnoreCase(result + "");
        } finally {
            quietlyCloseJedis(jedis);
        }
    }

    /**
     * 釋放鎖.
     *
     * @param lockKey 鎖的鍵值
     * @param value   鎖的值.必需要和上鎖的值一致.
     */
    public void unlock(String lockKey, String value) {
        Jedis jedis = jedisPool.getResource();
        try {
            jedis.eval(UNLOCK_LUA, 1, lockKey, value);
        } finally {
            quietlyCloseJedis(jedis);
        }
    }

    /**
     * 關閉redis鏈接。jedis 3.0開始,官方拋棄了原來的returnResource和returnBrokenResource。
     *
     * @param jedis
     */
    private void quietlyCloseJedis(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }

}
相關文章
相關標籤/搜索