Redis实现分布式锁

2018-03-21 21:34:41
866次阅读
0个评论
使用java自带的lock框架可以很好的解决并发问题,但如果是分布式系统的话,就不能通过这个方法解决并发问题,此时可以使用redis实现并发锁来解决这个问题



使用redis锁有两个重要函数需要介绍


SETNX命令(SET if Not EXists)
语法:
SETNX key value
功能:
当且仅当 key 不存在,将 key 的值设为 value ,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。

GETSET命令
语法:
GETSET key value
功能:
将给定 key 的值设为 value ,并返回 key 的旧值 (old value),当 key 存在但不是字符串类型时,返回一个错误,当key不存在时,返回空。



使用java实现获取锁操作时,只需要使用setnx命令向redis中插值即可,(同时设置锁的最大持有时间,防止系统崩溃导致一直持有锁)代码片段如下:


/** 
     * 默认每个线程持有锁超时时间 30s 
     */  
    private static final long LOCK_EXPIRE = 30;  
      
    /** 
     * redis锁前缀 
     */  
    private static final String LOCK_PREFIX = "LOCK:";  
  
  
 /** 
     * 在指定时间内尝试获取redis锁,成功返回true,失败返回false,并设置锁的过期时间 
     *  
     * @Description 
     * @param key 锁标识 
     * @param timeout 等待获取锁的最大时间(秒) 
     * @param expire 锁过期失效时间(秒) 
     * @return 
     */  
    public boolean lock(String key, long timeout, long expire)  
    {  
        long begin = 0;  
        do  
        {  
            if (tryLock(key, expire))  
            {  
                return true;  
            }  
            begin += 3;  
            try  
            {  
                Thread.sleep(3000);  
            }  
            catch (InterruptedException e)  
            {  
                logger.error("获取锁出错", e);  
                return false;  
            }  
        } while (begin <= timeout);  
        return false;  
    }  
      
    /** 
     * 尝试获取redis锁,成功返回true,失败返回false,并设置锁的过期时间 
     *  
     * @Description 
     * @param key 锁标识 
     * @param expire 锁过期失效时间(秒) 
     * @return 
     */  
    public boolean tryLock(String key, long expire)  
    {  
        logger.info("try get lock for:" + key);  
        key = LOCK_PREFIX + key;  
        RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection();  
        if (redisConnection.setNX(key.getBytes(), key.getBytes()))  
        {  
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);  
            redisConnection.close();  
            return true;  
        }  
        redisConnection.close();  
        return false;  
    }  
      
    /** 
     * 在指定时间内尝试获取redis锁,成功返回true,失败返回false 
     *  
     * @Description 
     * @param key 锁标识 
     * @param timeout 等待获取锁的最大时间(秒) 
     * @return 
     */  
    public boolean lock(String key, long timeout)  
    {  
        return lock(key, timeout, LOCK_EXPIRE);  
    }  
      
    /** 
     * 尝试获取redis锁,成功返回true,失败返回false 
     *  
     * @Description 
     * @param key 锁标识 
     * @return 
     */  
    public boolean tryLock(String key)  
    {  
        return tryLock(key, LOCK_EXPIRE);  
    }  
      
    /** 
     * redis锁解锁 
     *  
     * @Description 
     * @author congyue.lu 
     * @param key 锁标识 
     */  
    public void unLock(String key)  
    {  
        key = LOCK_PREFIX + key;  
        redisTemplate.delete(key);  
    }
redis加锁部分代码可以使用以下方式替换:


public boolean tryLock(String key, long expire)  
{  
    logger.info("try get lock for:" + key);  
    key = LOCK_PREFIX + key;  
    final String keyStore = key;  
    boolean result = redisTemplate.execute(new RedisCallback<Boolean>()  
    {  
        @Override  
        public Boolean doInRedis(RedisConnection connection)  
            throws DataAccessException  
        {  
            Boolean result = connection.setNX(keyStore.getBytes(), keyStore.getBytes());  
            if (result)  
            {  
                connection.expire(keyStore.getBytes(), expire);  
            }  
            return result;  
        }  
    });  
    return result;  
}

使用如上代码时,不必显示的关闭connection


收藏00

登录 后评论。没有帐号? 注册 一个。