如何实现分布式锁

作者: 不孤独的字符串 | 来源:发表于2019-02-27 21:35 被阅读1次

    谈及分布式,总会帮随着资源共享,然而有时候我们却希望只有一个线程对资源进行调度,不希望有多个线程同步调度,比如:每天晚上定时统计网站的访客量,这就涉及到了分布式锁。

    因此我们要求这把锁要有以下的特性:

    • 保证在分布式部署的应用集群中,同一时刻同一个方法只能有一个线程执行
    • 这把锁要是一把可重入锁(避免死锁)
    • 有高性能和高可用的获取锁和释放锁功能

    了解过Redis的人可能都知道Redis是单线程的,也就是对于同一个Redis实例,同一时刻只会处理一个请求。因为对于分布式架构不同机器共享同一资源,我们可以借助Redis实现分布式锁。使用Redis的SETNX和GETSET的配合使用则可实现分布式锁。

    命令介绍
    • setnx(key, value):给指定的key设置value,当且仅当key不存在时,key会被设置,返回了1,否则不做任何动作,返回0
    • getset(key, value):给指定key设置新的value,同时返回旧的value

    多个线程执行以下操作:

    long currentTime = System.currentTimeMillis();
    String lockTime = String.valueOf(currentTime + Lock_Timeout + 1);//锁的有效时间
    Long result = jedisClient.setnx(key, lockTime);
    

    如果 result == 1,说明该进程获得锁,setnx 将 key 的值设置为锁的超时时间(当前时间 + 锁的有效时间 + 1)。
    如果 result == 0,说明其他进程已经获得了锁,进程可以在一个循环中不断地尝试 SETNX 操作,以获得锁。

    考虑一种情况,如果进程获得锁后,断开了与 Redis 的连接(可能是进程挂掉,或者网络中断),如果没有有效的释放锁的机制,那么其他进程都会处于一直等待的状态,即出现“死锁”。对此,我们可以通过 getset 获取锁的一个最新的被改变的时间,与当前的时间进行比较,进而校验锁的一个最新的状态。

    //获取锁
    private boolean innerGetLock(String key){
        long currentTime = System.currentTimeMillis();
        String lockTime = String.valueOf(currentTime + Lock_Timeout + 1);//锁的有效时间
        Long result = jedisClient.setnx(key, lockTime);
        if (result == 1) {
            return true;
        } else {
            if (currentTime > Long.valueOf(jedisClient.get(key))){
                //当前时间已超过锁的有效时间,则判断是否有其他进程已获取到锁
                String preLockTimeDuration = jedisClient.getSet(key, lockTime);
                if (currentTime > Long.valueOf(preLockTimeDuration)) {
                    return true;
                }
            }
            return false;
        }
    }
    

    相关文章

      网友评论

        本文标题:如何实现分布式锁

        本文链接:https://www.haomeiwen.com/subject/ldyquqtx.html