基于Redis实现分布式锁

之前项目中使用redis锁实现秒杀等一些并发业务,在这里整理一下基于Redis实现分布式锁的简单入门实例,记录一下,便于以后查看、学习。

SpringBoot集成redisson分布式锁:

https://www.cnblogs.com/yangzhilong/p/7605807.html

https://blog.csdn.net/sinat_25295611/article/details/80420086

https://www.cnblogs.com/fswhq/p/9668326.html

https://blog.csdn.net/qq_33814088/article/details/83347757

参考博客:

https://blog.csdn.net/l_bestcoder/article/details/79336986

https://blog.csdn.net/starlh35/article/details/79759630

1、简介

在分布式系统中存在并发场景,为了解决这一问题,基于redis锁一定程度可以解决这一问题,但是也有缺点,如死锁等。。。,

这块的优缺点,大家自行百度学习一下。。。这里主要介绍的基于redis实现锁的机制!

分布式系统实现的时候要注意的几个关键点:

1、锁信息必须是会过期超时的,不能让一个线程长期占有一个锁而导致死锁;

2、同一时刻只能有一个线程获取到锁。

主要实现锁功能的redis命令:

setnx(key, value):“set if not exits”,若该key-value不存在,则成功加入缓存并且返回1,否则返回0。

get(key):获得key对应的value值,若不存在则返回nil。

getset(key, value):先获取key对应的value值,若不存在则返回nil,然后将旧的value更新为新的value。

expire(key, seconds):设置key-value的有效期为seconds秒,避免死锁

delete (key):删除key

2、实现思路

a、加锁

当执行一个线程业务时,通过加锁(获取锁)实现防止别的线程去执行它;

获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。
获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。


b、释放锁

释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。

3、代码实现

其实代码实现大同小异,实现思想就是上面的思路。。。

/**
 * 实现 redis 加锁、释放锁
 */
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() < end) {
                if (conn.setnx(lockKey, identifier) == 1) {
                    conn.expire(lockKey, lockExpire);
                    // 返回value值,用于释放锁时间确认
                    retIdentifier = identifier;
                    return retIdentifier;
                }
                // 返回-1代表key没有设置超时时间,为key设置一个超时时间
                if (conn.ttl(lockKey) == -1) {
                    conn.expire(lockKey, lockExpire);
                }

                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
        return retIdentifier;
    }

    /**
     * 释放锁
     * 
     * @param lockName
     *            锁的key
     * @param identifier
     *            释放锁的标识
     * @return
     */
    public boolean releaseLock(String lockName, String identifier) {
        Jedis conn = null;
        String lockKey = "lock:" + lockName;
        boolean retFlag = false;
        try {
            conn = jedisPool.getResource();
            while (true) {
                // 监视lock,准备开始事务
                conn.watch(lockKey);
                // 通过前面返回的value值判断是不是该锁,若是该锁,则删除,释放锁
                if (identifier.equals(conn.get(lockKey))) {
                    Transaction transaction = conn.multi();
                    transaction.del(lockKey);
                    List<Object> 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;
    }
}
4、测试案例

模拟线程并发业务。。。

线程服务

加锁后运行测试:  20个子线程业务被顺序执行

 不加锁后运行测试:  20个子线程业务被非顺序执行,谁抢到资源谁先执行。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值