基于redis实现的分布式锁

RedisLockHelper.java


/**
 * 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);
        }
    }
}

原文地址:https://www.cnblogs.com/qixidi/p/10213554.html

时间: 2024-11-05 16:36:18

基于redis实现的分布式锁的相关文章

基于Redis的简单分布式锁的原理

参考资料:https://redis.io/commands/setnx 加锁是为了解决多线程的资源共享问题.Java中,单机环境的锁可以用synchronized和Lock,其他语言也都应该有自己的加锁机制.但是到了分布式环境,单机环境中的锁就没什么作用了,因为每个节点只能获取到自己机器内存中的锁,而无法获取到其他节点的锁状态. 分布式环境中,应该用专门的分布式锁来解决需要加锁的问题.分布式锁有很多实现,Redis,zookeeper都可以.这里以Redis为例,讲述一下基于Redis的分布式

redis系列:分布式锁

redis系列:分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分为两部分,一个是单机环境,另一个是集群环境下的Redis锁实现.在介绍分布式锁的实现之前,先来了解下分布式锁的一些信息. 2 分布式锁 2.1 什么是分布式锁? 分布式锁是控制分布式系统或不同系统之间共同访问共享资源的一种锁实现,如果不同的系统或同一个系统的不同主机之间共享了某个资源时,往往需要互斥

阿里JAVA面试题剖析:一般实现分布式锁都有哪些方式?使用 Redis 如何设计分布式锁?

面试原题 一般实现分布式锁都有哪些方式?使用 redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高? 面试官心理分析 其实一般问问题,都是这么问的,先问问你 zk,然后其实是要过度到 zk 关联的一些问题里去,比如分布式锁.因为在分布式系统开发中,分布式锁的使用场景还是很常见的. 面试题剖析 Redis 分布式锁 官方叫做 RedLock 算法,是 Redis 官方支持的分布式锁算法. 这个分布式锁有 3 个重要的考量点: 互斥(只能有一个客户端

如何用redis正确实现分布式锁?

先把结论抛出来:redis无法正确实现分布式锁!即使是redis单节点也不行!redis的所谓分布式锁无法用在对锁要求严格的场景下,比如:同一个时间点只能有一个客户端获取锁. 首先来看下单节点下一般redis分布式锁的实现,其实就是个set: 加锁: /** * 尝试获取分布式锁 * @param jedis Redis客户端 * @param lockKey 锁 * @param requestId 请求标识 * @param expireTime 超期时间 * @return 是否获取成功

如何用 redis 造一把分布式锁

基本概念 锁 wiki:In computer science, a lock or mutex (from mutual exclusion) is a synchronization mechanism for enforcing limits on access to a resource in an environment where there are many threads of execution. A lock is designed to enforce a mutual e

Redis .net 客户端 分布式锁

关于Redis分布式锁的参考链接:http://redis.io/topics/distlock. 在我们项目中,之前琢磨用:ServiceStack.Redis,发现ServiceStack.Redis收费的,每小时内操作6000次以上报错:“The free-quota limit on '6000 Redis requests per hour' has been reached……” 那么看看别的Redis客户端: StackExchange.Redis貌似不支持分布式锁,还是我不知道?

使用redis构建可靠分布式锁

关于分布式锁的概念,具体实现方式,直接参阅下面两个帖子,这里就不多介绍了. 分布式锁的多种实现方式 分布式锁总结 对于分布式锁的几种实现方式的优劣,这里再列举下 1. 数据库实现方式 优点:易理解 缺点:操作数据库消耗较大,性能较低.为了处理一些异常,会使得整个方案越来越复杂 2. 缓存实现方式 优点:性能好,实现起来较为方便. 缺点:通过超时时间来控制锁的失效时间并不是十分的靠谱. 3 zookeeper实现 优点:有效的解决单点问题,不可重入问题,非阻塞问题以及锁无法释放的问题. 缺点:性能

基于zookeeper简单实现分布式锁

这里利用zookeeper的EPHEMERAL_SEQUENTIAL类型节点及watcher机制.来简单实现分布式锁. 主要思想: 1.开启10个线程.在disLocks节点下各自创建名为sub的EPHEMERAL_SEQUENTIAL节点. 2.获取disLocks节点下全部子节点,排序,假设自己的节点编号最小,则获取锁: 3.否则watch排在自己前面的节点,监听到其删除后,进入第2步(又一次检測排序是防止监听的节点发生连接失效.导致的节点删除情况): 4.删除自身sub节点,释放连接: 这

Spark生态系统解析及基于Redis的开源分布式服务Codis

摘要:在第九期"七牛开发者最佳实践日"上,陈超就Spark整个生态圈进行了讲解,而刘奇则分享豌豆荚在Redis上的摸索和实践. 1月24日,一场基于Spark和Redis组成的分布式系统实践分享由Spark资深布道者陈超和豌豆荚资深系统架构师刘奇联手打造. 陈超:Spark Ecosystem & Internals 陈超(@CrazyJvm),Spark布道者 在分享中,陈超首先简短的介绍了Spark社区在2014年的发展:目前Spark的发布版本是1.2,整个2014年Sp