`

浅谈redis实现的分布式锁

 
阅读更多
/**
 * Created by BingZhong on 2017/7/29.
 *
 * 基于Redis实现的分布式锁
 */
public final class RedisLockHelper {

    private static Logger logger = LoggerFactory.getLogger(RedisLockHelper.class);

    /**
     * redis操作帮助类,可以是其他封装了redis操作的类
     */
    private RedisHelper redisHelper;

    public static final long DEFAULT_TIMEOUT = 30 * 1000;

    public static final long DEFAULT_SLEEP_TIME = 100;

    private RedisLockHelper(RedisHelper redisHelper) {
        this.redisHelper = redisHelper;
    }

    public static RedisLockHelper getInstance(RedisHelper redisHelper) {
        return new RedisLockHelper(redisHelper);
    }

    /**
     * 创建锁
     *
     * @param mutex     互斥量
     * @param timeout   锁的超时时间
     * @param sleepTime 线程自旋尝试获取锁时的休眠时间
     * @param timeUnit  时间单位
     */
    public RedisLock newLock(String mutex, long timeout, long sleepTime, TimeUnit timeUnit) {
        logger.info("创建分布式锁,互斥量为{}", mutex);
        return new RedisLock(mutex, timeout, sleepTime, timeUnit);
    }

    public RedisLock newLock(String mutex, long timeout, TimeUnit timeUnit) {
        return newLock(mutex, timeout, DEFAULT_SLEEP_TIME, timeUnit);
    }

    public RedisLock newLock(String mutex) {
        return newLock(mutex, DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
    }

    public class RedisLock {
        /**
         * 用于创建redis健值对的键,相当于互斥量
         */
        private final String mutex;

        /**
         * 锁过期的绝对时间
         */
        private volatile long lockExpiresTime = 0;

        /**
         * 锁的超时时间
         */
        private final long timeout;

        /**
         * 每次循环获取锁的休眠时间
         */
        private final long sleepTime;

        /**
         * 锁的线程持有者
         */
        private volatile Thread lockHolder = null;

        private final ReentrantLock threadLock = new ReentrantLock();

        public RedisLock(String mutex, long timeout, long sleepTime, TimeUnit timeUnit) {
            this.mutex = mutex;
            this.timeout = timeUnit.toMillis(timeout);
            this.sleepTime = timeUnit.toMillis(sleepTime);
        }

        /**
         * 加锁,将会一直尝试获取锁,直到超时
         */
        public boolean lock(long acquireTimeout, TimeUnit timeUnit) throws InterruptedException {
            acquireTimeout = timeUnit.toMillis(acquireTimeout);
            long acquireTime = acquireTimeout + System.currentTimeMillis();
            threadLock.tryLock(acquireTimeout, timeUnit);
            try {
                while (true) {
                    boolean hasLock = tryLock();
                    if (hasLock) {
                        //获取锁成功
                        return true;
                    } else if (acquireTime < System.currentTimeMillis()) {
                        break;
                    }
                    Thread.sleep(sleepTime);
                }
            } finally {
                if (threadLock.isHeldByCurrentThread()) {
                    threadLock.unlock();
                }
            }

            return false;
        }

        /**
         * 尝试获取锁,无论是否获取到锁都将直接返回而不会阻塞
         * 不支持重入锁
         */
        public boolean tryLock() {
            if (lockHolder == Thread.currentThread()) {
                throw new IllegalMonitorStateException("不支持重入锁");
            }
            long currentTime = System.currentTimeMillis();
            String expires = String.valueOf(timeout + currentTime);
            //尝试设置互斥量
            if (redisHelper.setNx(mutex, expires) > 0) {
                setLockStatus(expires);
                return true;
            } else {
                String currentLockTime = redisHelper.get(mutex);
                //检查锁是否超时
                if (Objects.nonNull(currentLockTime) && Long.parseLong(currentLockTime) < currentTime) {
                    //获取旧的锁时间并设置互斥量
                    String oldLockTime = redisHelper.getSet(mutex, expires);
                    //判断获取到的旧值是否一致,不一致证明已经有另外的进程(线程)成功获取到了锁
                    if (Objects.nonNull(oldLockTime) && Objects.equals(oldLockTime, currentLockTime)) {
                        setLockStatus(expires);
                        return true;
                    }
                }

                return false;
            }
        }

        /**
         * 该锁是否被锁住
         */
        public boolean isLock() {
            String currentLockTime = redisHelper.get(mutex);
            //存在互斥量且锁还为过时即锁住
            return Objects.nonNull(currentLockTime) && Long.parseLong(currentLockTime) > System.currentTimeMillis();
        }

        public String getMutex() {
            return mutex;
        }

        /**
         * 解锁
         */
        public boolean unlock() {
            //只有锁的持有线程才能解锁
            if (lockHolder == Thread.currentThread()) {
                //判断锁是否超时,没有超时才将互斥量删除
                if (lockExpiresTime > System.currentTimeMillis()) {
                    redisHelper.del(mutex);
                    logger.info("删除互斥量[{}]", mutex);
                }
                lockHolder = null;
                logger.info("释放[{}]锁成功", mutex);

                return true;
            } else {
                throw new IllegalMonitorStateException("没有获取到锁的线程无法执行解锁操作");
            }
        }

        private void setLockStatus(String expires) {
            lockExpiresTime = Long.parseLong(expires);
            lockHolder = Thread.currentThread();
            logger.info("获取[{}]锁成功", mutex);
        }
    }
}

分享到:
评论

相关推荐

    浅谈Redis分布式锁的正确实现方式

    虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁。 可靠性 首先,为了确保分布式锁可用,我们至少要确保...

    浅谈分布式锁

    1. RedLock算法:它通过多个独立的Redis节点来实现锁的可靠性,增加锁的抗故障能力。使用RedLock算法需要多个Redis实例,客户端必须获取到超过半数的锁才能认为成功获取锁。但RedLock在某些极端情况下可能无法保证锁...

    浅谈Redis在分布式系统中的协调性运用

    Redis提供`SETNX`(Set If Not Exists)和`EXPIRE`命令组合,可以创建临时且唯一的键,从而实现锁的功能。此外,利用Lua脚本的原子执行特性,可以进一步确保锁操作的完整性。 其次,Redis的发布/订阅(Pub/Sub)...

    浅谈分布式锁的几种使用方式(redis、zookeeper、数据库)

    Redis是一个内存数据结构存储系统,其速度非常快,适合实现锁。使用Redis实现分布式锁通常有两种方法:`SETNX`命令和`lua`脚本。`SETNX`命令在键不存在时设置键值,实现互斥锁。为了防止锁不能被释放(例如,客户端...

    【ASP.NET编程知识】浅谈ASP.NET Core中间件实现分布式 Session.docx

    实现分布式 Session 首先需要安装相应的包,例如对于 Redis,可以使用 Microsoft.AspNetCore.Session.Redis 包。 1.3.1. 添加依赖 在项目的 `csproj` 文件中添加对 Redis 的 NuGet 包引用: ```xml ``` 替换 ...

    浅谈电商支撑系统中分布式缓存管理技术的研究与实现.pdf

    在分布式缓存技术的研究与实现中,Redis作为一种高性能的键值存储数据库,广泛应用于分布式缓存领域。它支持多种数据结构,如字符串、哈希、列表、集合等,适用于不同的应用场景。分布式缓存的主要配置方式有主从...

    浅谈技术团队管理认知背后的哲学思考.pptx

    在实践中,基于Redis的分布式锁方案被广泛采用,利用SETNX命令确保在key不存在时设置值,并通过设置过期时间来控制锁的生命周期。然而,这种方案并非无懈可击,存在如单点故障、锁时间不可控和无法自动续租等问题,...

    浅谈redis在项目中的应用

    7. 分布式特性:Redis支持数据的主从复制和高可用集群。这使得Redis能够适用于大规模数据存储和高并发访问,增加了系统的可靠性和稳定性。 8. 序列化:在项目应用中,涉及到将数组等非字符串类型的数据存储到Redis...

    浅谈ASP.NET Core中间件实现分布式 Session

    为了解决这个问题,ASP.NET Core 提供了分布式 Session,它能够将 Session 数据存储在分布式缓存中,如 Redis 或 SQL Server,以实现跨服务器的数据共享。 **1. 分布式 Session 的工作原理** 分布式 Session 利用...

    浅谈java实现redis的发布订阅(简单易懂)

    * 分布式架构:在分布式架构中,Redis 的发布订阅机制可以用于实现数据的实时同步。 * 博客网站:在博客网站中,使用 Redis 的发布订阅机制,可以实时推送新文章给订阅者。 五、总结 本篇文章详细介绍了 Redis 的...

    浅谈高并发下接口幂等性解决方案.docx

    浅谈高并发下接口幂等性解决方案 幂等性是在编程中一个非常重要的概念,它是指一个操作,不论执行多少次,产生的效果和返回的结果都是一样的。幂等函数或幂等方法是指可以使用相同参数重复执行,并能获得相同结果的...

    Redis基础笔记总结

    #### 三、Redis迭代演化和Redis7新特性浅谈 - **时间推移与版本升级**: - 官方博客: - 版本迭代历程中的一些重要里程碑。 - **Redis7.0新特性**: - **Redis Functions**: 提供函数调用的能力,扩展Redis的功能...

    缓存技术浅谈

    这篇“缓存技术浅谈”可能是一篇深入解析缓存概念、原理及应用的博客文章,结合PPT形式提供了更直观的理解。以下是根据标题和描述可能涉及的一些核心知识点: 1. **缓存基本概念**:缓存是一种存储技术,用于临时...

    浅谈网站架构中缓存的应用

    缓存雪崩则是大量缓存同时失效,造成系统崩溃,可以通过设置合理的过期时间、加锁机制或者分布式锁来避免。 总的来说,网站架构中的缓存应用是一门深奥的学问,需要根据业务场景和性能需求,选择合适的缓存类型、...

    Web应用服务器缓存浅谈PPT

    标题:“Web应用服务器缓存浅谈PPT” 在Web开发中,服务器缓存是一种提高性能和响应速度的关键技术。本讲座将探讨Web应用服务器缓存的基础知识,包括其工作原理、常见类型和最佳实践。通过学习,我们可以更好地理解...

Global site tag (gtag.js) - Google Analytics