JAVA程序员

Redis实现分布式锁

2017-10-16  本文已影响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文件。

上一篇下一篇

猜你喜欢

热点阅读