zoukankan      html  css  js  c++  java
  • 基于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);
            }
        }
    }
    
    
  • 相关阅读:
    递归调用简单的讲解
    有关杭电acm问题的分类
    【jquery】切换标题与内容/点击后动态切换当前状态
    将数组某键值抽出作为新数组
    table表格制作
    如何导入大sql文件到mysql数据库
    二维数组按照某一键值进行排序
    利用PHP输出某一目录所有文件
    php结合js动态获取空间时间
    ie6不支持minheight的解决方案
  • 原文地址:https://www.cnblogs.com/qixidi/p/10213554.html
Copyright © 2011-2022 走看看