美文网首页JAVA程序员
Redis实现分布式锁

Redis实现分布式锁

作者: 刺風 | 来源:发表于2017-10-16 11:04 被阅读69次
    一、分布式锁是啥?
    1. 单机锁的概念:我们正常跑的单机项目(也就是在tomcat下跑一个项目不配置集群)想要在高并发的时候加锁很容易就可以搞定,java提供了很多的机制例如:synchronized、volatile、ReentrantLock等锁的机制。
    2. 为啥需要分布式锁:当我们的项目比较庞大的时候,单机版的项目已经不能满足吞吐量的需求了,需要对项目做负载均衡,有可能还需要对项目进行解耦拆分成不同的服务,那么肯定是做成分布式的项目,分布式的项目因为是不同的程序控制,所以使用java提供的锁并不能完全保证并发需求,需要借助第三方的框架来实现对并发的阻塞控制,来满足实际业务的需要。
    二、话不多说,直接上代码:
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    /**
     * @ClassName RedisLock
     * @Description 
     * @author 刺风
     * @date 2017-09-30 18:07
     */
    public class RedisLock {
        private static Logger log = LoggerFactory.getLogger(RedisLock.class);
        private String lockKey;
        private int expireMsecs= 1000;
        private volatile boolean locked = false;
        private RedisManager redisManager;
    
    
        public RedisLock( RedisManager redisManager,String lockKey) {
            this.lockKey = lockKey;
            this.redisManager = redisManager;
        }
    
        public RedisLock(RedisManager redisManager, String lockKey, int expireMsecs) {
            this.expireMsecs=expireMsecs;
            this.redisManager=redisManager;
            this.lockKey = lockKey;
        }
    
        /**
         * @Title lock
         * @Description 获取锁
         * 实现思路:在指定的重试次数中,客户端会去重试获取锁
         * 如果当前没有客户端使用锁,则新创建一把锁;如果已经产生锁了,并且不在当前客户端手中
         * 则去验证上把锁的到期时间是否过期,如果已过期则通过getset方式重新获取锁,
         * 并设定新锁的到期时间;没有获取锁的线程则在指定的重试次数里去尝试获取锁,
         * 直至上把锁的到期时间已过期(肯定会过期,因为设置的过期时间远小于等待重试的随机时间),
         * 会再次选一个线程来去获取锁,再有其他客户端连进来还是这个规律。
         * @author 刺风
         * @return boolean
         */
        public synchronized boolean lock() throws InterruptedException {
            //如果在10次重试也没获取到锁,那么只能表示网络繁忙,请稍后再试了,不过这是不可能发生的事情,
            // 循环会随机睡眠100~1000毫秒,必然会重新获取锁的
            int cycle = 1;//循环次数
            while (cycle <= 10) {
                log.info("当前客户端尝试获取分布式锁,第"+cycle+"次");
                long expires = System.currentTimeMillis() + expireMsecs + 1;
                String expiresStr = String.valueOf(expires); //锁到期时间
                if (redisManager.setnx(lockKey, expiresStr)) {
                    log.info("客户端获取分布式锁成功");
                    locked = true;
                    return true;
                }
                //获取上次上锁的时间
                String currentValueStr = redisManager.get(lockKey);
                //判断是否为空,不为空的情况下,当前时间大于上次的锁到期时间 表示已经过期 则需要重新获取锁,并设置新的锁到期时间
                log.info("获取上把锁的过期时间:"+currentValueStr+",和当前时间进行比较,判断是否过期");
                if (currentValueStr != null && System.currentTimeMillis()>Long.parseLong(currentValueStr) ) {
                    String oldValueStr = redisManager.getSet(lockKey, expiresStr);//获取上一个锁到期时间,并设置现在的锁到期时间
                    // 如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁
                    if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
                        log.info("上把锁的已过期,当前客户端已重新获取锁,并设置了到期时间");
                        locked = true;
                        return true;
                    }
                }
                //循环里面随机数是100~1000毫秒 循环10次等待时间就是10*(100~100)毫秒,所以肯定会获取到锁的,因为超时时间在外面就设置了1000毫秒
                int num=(int)(100+Math.random()*(1000-1+1));
                log.info("给当前线程随机生成睡眠时间:"+num+"毫秒");
                Thread.sleep(num);
                cycle++;
            }
            return false;
        }
    
        /**
         * @Title unlock
         * @Description 释放锁
         * @author 刺风
         */
        public synchronized void unlock() {
            if (locked) {
                log.info("当前客户端释放分布式锁");
                redisManager.del(lockKey);
                locked = false;
            }
        }
    
    }
    

    RedisManager:

    package com.wtoip.platform.porsche.web.common;
    
    import com.alibaba.fastjson.parser.ParserConfig;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Repository;
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.exceptions.JedisException;
    
    import java.util.*;
    
    @Repository
    public class RedisManager {
      
        @Autowired
        JedisPool jedisPool;
    
        public Long del(String key) {
            Jedis jedis = jedisPool.getResource();
            try {
                return jedis.del(key);
            } catch (JedisException e) {
    
            } finally {
                jedisPool.returnResource(jedis);
            }
            return 0L;
        }
    
        /**
         * 判断key是否存在
         * @param key
         * @return
         */
        public boolean containsKey(final String key) {
            Jedis jedis = jedisPool.getResource();
            try {
                return jedis.exists(key);
            } catch (JedisException e) {
    
            } finally {
                jedisPool.returnResource(jedis);
            }
            return false;
        }
    
        /**
         * 获取Map值
         * @param key
         * @param filed
         * @return
         */
        public String hget(String key,String filed) {
            Jedis jedis = jedisPool.getResource();
            String str = "";
            try {
                str = jedis.hget(key,filed);
                return str;
            } catch (JedisException e) {
    
            } finally {
                jedisPool.returnResource(jedis);
            }
            return str;
        }
        public boolean hdel(String key,String filed) {
            Jedis jedis = jedisPool.getResource();
            try {
                jedis.hdel(key,filed);
                return true;
            } catch (JedisException e) {
    
            } finally {
                jedisPool.returnResource(jedis);
            }
            return false;
        }
    
    
        /**
         * 获取Map值
         * @param key
         * @return
         */
        public Map<String, String> hGetAll(String key) {
            Jedis jedis = jedisPool.getResource();
            Map<String, String> map = new HashMap<String,String>();
            try {
                map = jedis.hgetAll(key);
                return map;
            } catch (JedisException e) {
    
            } finally {
                jedisPool.returnResource(jedis);
            }
            return map;
        }
        /**
         * 获取Map值
         * @param key
         * @return
         */
        public  Map<String, Object> getHash(String key) {
            Jedis jedis = jedisPool.getResource();
            final byte[] rawKey = SerializerUtil.rawKey(key);
            Map<String, Object> res = new HashMap<String, Object>();
            try {
                Map<byte[], byte[]> map = jedis.hgetAll(rawKey);
                return SerializerUtil.deserializeHashMap(map);
            } catch (JedisException e) {
    
            } finally {
                jedisPool.returnResource(jedis);
            }
            return res;
        }
    
    
    
    
        public boolean hexists(String key, String value){
            Jedis jedis = jedisPool.getResource();
            try {
                return jedis.hexists(key,value);
            } catch (JedisException e) {
    
            } finally {
                jedisPool.returnResource(jedis);
            }
            return false;
        }
    
        /**
         * 设置Map值
         * @param key
         * @param value
         * @return
         */
        public boolean hset(String key,String filed, String value) {
            Jedis jedis = jedisPool.getResource();
            try {
                jedis.hset(key,filed,value);
                return true;
            } catch (JedisException e) {
    
            } finally {
                jedisPool.returnResource(jedis);
            }
            return false;
        }
        public boolean hincrBy(String key,String filed, Integer value) {
            Jedis jedis = jedisPool.getResource();
            try {
                jedis.hincrBy(key,filed,value);
                return true;
            } catch (JedisException e) {
    
            } finally {
                jedisPool.returnResource(jedis);
            }
            return false;
        }
        /**
         * 设置Map值
         * @param key
         * @param value
         * @return
         */
        public boolean hsetObject(String key,String filed, Object value) {
            Jedis jedis = jedisPool.getResource();
            final byte[] rawKey = SerializerUtil.rawKey(key);
            final byte[] rawFiled = SerializerUtil.rawKey(filed);
            final byte[] rawValue = SerializerUtil.rawValue(value);
    
            try {
                jedis.hset(rawKey,rawFiled,rawValue);
    
                return true;
            } catch (JedisException e) {
    
            } finally {
                jedisPool.returnResource(jedis);
            }
            return false;
        }
    
        public Object hgetObject(String key,String filed) {
            Object obj=null;
            Jedis jedis = jedisPool.getResource();
            final byte[] rawKey = SerializerUtil.rawKey(key);
            final byte[] rawFiled = SerializerUtil.rawKey(filed);
            try {
               obj=SerializerUtil.deserializeValue(jedis.hget(rawKey,rawFiled));
            } catch (JedisException e) {
    
            } finally {
                jedisPool.returnResource(jedis);
            }
            return obj;
        }
    
    
    
        /**
         * 删除map指定值
         * @param key
         * @param value
         * @return
         */
        public boolean hdelObject(String key,String filed) {
            Jedis jedis = jedisPool.getResource();
            final byte[] rawKey = SerializerUtil.rawKey(key);
            final byte[] rawFiled = SerializerUtil.rawKey(filed);
            try {
                jedis.hdel(rawKey,rawFiled);
                return true;
            } catch (JedisException e) {
    
            } finally {
                jedisPool.returnResource(jedis);
            }
            return false;
        }
    
    
    
        /**
         * 设置HASH值(同时设置过期时间)
         * @param key
         * @param value
         * @return
         */
        public long hset(String key,String filed, String value,long unixTime) {
            if (StringUtils.isBlank(key)) {
                return 0;
            }
            Jedis jedis = jedisPool.getResource();
            try {
                jedis.hset(key,filed,value);
                return jedis.expireAt(key, unixTime);
            } catch (JedisException e) {
    
            } finally {
                jedisPool.returnResource(jedis);
            }
            return 0;
        }
    
        /**
         * 获取值
         * @param key
         * @return
         */
        public String get(String key) {
            Jedis jedis = jedisPool.getResource();
            try {
                return jedis.get(key);
            } catch (JedisException e) {
    
            } finally {
                jedisPool.returnResource(jedis);
            }
            return "";
        }
    
        /**
         * 判断是否存在该key
         * @param key
         * @return true|false
         */
        public boolean exists(String key) {
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                return jedis.exists(key);
            } catch (JedisException e) {
               e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return false;
        }
    
    
        /**
         * 设置值
         * @param key
         * @param value
         * @return
         */
        public boolean set(String key, String value) {
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                jedis.set(key, value);
                return true;
            } catch (JedisException e) {
             e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return false;
        }
        /**
         * 设置值
         * @param key
         * @param value
         * @return
         */
        public boolean setObject(String key, Object value) {
            final byte[] rawKey = SerializerUtil.rawKey(key);
            final byte[] rawValue = SerializerUtil.rawValue(value);
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                jedis.set(rawKey, rawValue);
                return true;
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return false;
        }
        /**
         * 获取值
         * @param key
         * @return
         */
        public Object getObject(String key) {
            final byte[] rawKey = SerializerUtil.rawKey(key);
            Jedis jedis = jedisPool.getResource();
            try {
                return SerializerUtil.deserializeValue(jedis.get(rawKey));
            } catch (JedisException e) {
    
            } finally {
                jedisPool.returnResource(jedis);
            }
            return "";
        }
    
        /**
         * 移除有序集合中给定的排名区间的所有成员
         * @param key
         * @param start
         * @param end
         * @return
         */
        public Long zremrangeByRank(String key, long start, long end) {
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                return jedis.zremrangeByRank(key, start, end);
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return 0L;
        }
    
        /**
         * 向有序集合添加一个成员
         * @param key
         * @param score
         * @param member
         * @return
         */
        public Long zadd(String key, double score, String member) {
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                return jedis.zadd(key, score, member);
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return 0L;
        }
    
        /**
         * 向有序集合添加一个或多个成员,或者更新已存在成员的分数
         * @param key
         * @param scoreMembers
         * @return
         */
        public Long zadd(String key, Map<String, Double> scoreMembers) {
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                return jedis.zadd(key, scoreMembers);
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return 0L;
        }
    
        /**
         * 通过索引区间返回有序集合成指定区间内的成员
         * @param key
         * @param start
         * @param end
         * @return
         */
        public Set<String> zrange(String key, long start, long end) {
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                return jedis.zrange(key, start, end);
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return null;
        }
    
        public Set<String> zrevrange(String key, long start, long end) {
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                return jedis.zrevrange(key, start, end);
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return null;
        }
    
        public Long lpush(String key, String string) {
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                return jedis.lpush(key, string);
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return 0L;
        }
        public Long lPushObject(String key, Object value) {
            final byte[] rawKey = SerializerUtil.rawKey(key);
            final byte[] rawValue = SerializerUtil.rawValue(value);
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                return jedis.lpush(rawKey, rawValue);
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return 0L;
        }
    
        public Long rpush(String key, String string) {
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                return jedis.rpush(key, string);
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return 0L;
        }
        public Long rPushObject(String key, Object value) {
            final byte[] rawKey = SerializerUtil.rawKey(key);
            final byte[] rawValue = SerializerUtil.rawValue(value);
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                return jedis.rpush(rawKey, rawValue);
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return 0L;
        }
    
        public List<String> lrange(String key, long start, long end) {
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                return jedis.lrange(key, start, end);
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return new ArrayList<>();
        }
        public List<Object> lRangeObject(String key, long start, long end) {
            Jedis jedis = null;
            try {
                final byte[] rawKey = SerializerUtil.rawKey(key);
                jedis = jedisPool.getResource();
                List<byte[]>  list= jedis.lrange(rawKey, start, end);
                return deserializeValues(list, List.class);
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return new ArrayList<>();
        }
        public boolean lset(String key, long index, Object value) {
            Jedis jedis = null;
            try {
                final byte[] rawKey = SerializerUtil.rawKey(key);
                final byte[] rawValue = SerializerUtil.rawValue(value);
                jedis = jedisPool.getResource();
                String  res = jedis.lset(rawKey,index,rawValue);
                if(StringUtils.isNotBlank(res)&&"OK".equals(res)){
                    return true;
                }
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return false;
        }
    
        public Long llen(String key) {
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                return jedis.llen(key);
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                jedisPool.returnResource(jedis);
            }
            return 0L;
        }
    
        /**
         * 设置失效时间
         * @param key
         * @param second  失效时间 单位秒
         * @return
         */
        public Long expire(String key, Integer second) {
            Jedis jedis = jedisPool.getResource();
            Long status =0L;
            try {
                status = jedis.expire(key,second);
                return status;
            } catch (JedisException e) {
            } finally {
                jedisPool.returnResource(jedis);
            }
            return status;
        }
    
        private <T extends Collection<?>> T deserializeValues(Collection<byte[]> rawValues, Class<T> type) {
            Collection<Object> values = List.class.isAssignableFrom(type) ? new ArrayList<Object>(rawValues.size()) : new LinkedHashSet<Object>(rawValues.size());
            for (byte[] bs : rawValues) {
                values.add(SerializerUtil.deserializeValue(bs));
            }
            return (T) values;
        }
    
    
        public boolean setnx(String key, String value) {
            Jedis jedis = jedisPool.getResource();
            try {
                if (jedis.setnx(key, value) == 1) {
                    return true;
                }
            } catch (JedisException e) {
            } finally {
                jedisPool.returnResource(jedis);
            }
            return false;
        }
    
        public String getSet(String key, String value) {
            String oldvalue="";
            Jedis jedis = jedisPool.getResource();
            try {
               oldvalue=jedis.getSet(key,value);
            } catch (JedisException e) {
            } finally {
                jedisPool.returnResource(jedis);
            }
            return  oldvalue;
        }
    
    }
    
    三、注意事项

    在上面我贴出来RedisManager主要用了setnx和getSet方法,只要拿到了Jedis对象就可以调用了,具体根据自己的项目中Reids实现来去做就可以,不用拘泥于上述的RedisManager文件。

    相关文章

      网友评论

        本文标题:Redis实现分布式锁

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