redis实现分布式锁

2 篇文章 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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值