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();
        }
    }

}
相关文章
相关标签/搜索