zoukankan      html  css  js  c++  java
  • redis——分布式锁

    一、Jedis的简单创建

    package com.app.redis;
    
    import com.app.redis.lock.RedisWithLock;
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    import redis.clients.jedis.params.SetParams;
    
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * @author:wuqi
     * @date:2020/2/8
     * @description:com.app.redis
     * @version:1.0
     */
    public class RedisUtils {
    
        /**
         * 创建单例
         */
    
        private RedisUtils() throws IllegalAccessException {
            throw new IllegalAccessException();
        }
    
    //    private static Jedis JEDIS = null;
        private static JedisPool jedisPool = null;
        private static final String HOST = "192.168.0.114";
        static{
    //        JEDIS = new Jedis(HOST,6379,1000);
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxTotal(100);
            config.setMaxIdle(100);
            config.setMaxWaitMillis(10000);
            config.setTestOnBorrow(true);
            jedisPool = new JedisPool(config,HOST,6379);
        }
    
        private static Jedis getJedis(){
    //        return jedis;
            Jedis jedis = jedisPool.getResource();
            if (jedis != null){
                return jedis;
            }else {
                jedis = jedisPool.getResource();
                if(jedis != null){
                    return jedis;
                }
                return new Jedis(HOST,6379,1000);
            }
    
        }
    
        /**
         * 测试连接
         */
        public static void main(String[] args){
            Jedis jedis = null;
            try {
                jedis = getJedis();
                jedis.set("linkTest2","hello World2");
                String back = jedis.set("linkTest","hello World");
                System.out.println(("OK").equals(back));
                Object response = RedisUtils.eval(RedisWithLock.UNLOCK_EVAL, Arrays.asList("linkTest","linkTest2"), Arrays.asList("hello World","hello World2"));
                System.out.println(response);
            }finally {
                if(jedis != null){
                    //释放jedispool的一个连接
                    jedis.close();
                }
                //关闭jedispool
                close();
            }
        }
    
        /**
         * 封装方法
         */
    
        interface CallWithRedis<T>{
    
            public T call(Jedis jedis);
    
        }
    
        private static <T> T execute(CallWithRedis<T> caller){
            try (Jedis jedis = getJedis()){
                return caller.call(jedis);
            }
        }
    
        public static String set(String key, String value, SetParams params){
            return RedisUtils.execute(new CallWithRedis<String>() {
                @Override
                public String call(Jedis jedis) {
                    return jedis.set(key,value,params);
                }
            });
        }
    
        public static String get(String key){
            return RedisUtils.execute(new CallWithRedis<String>() {
                @Override
                public String call(Jedis jedis) {
                    return jedis.get(key);
                }
            });
        }
    
        public static Long del(String key){
            return RedisUtils.execute(new CallWithRedis<Long>() {
                @Override
                public Long call(Jedis jedis) {
                    return jedis.del(key);
                }
            });
        }
    
        public static Long expire(String key, int seconds){
            return RedisUtils.execute(new CallWithRedis<Long>() {
                @Override
                public Long call(Jedis jedis) {
                    return jedis.expire(key,seconds);
                }
            });
        }
    
        public static Object eval(String script, List<String> keys, List<String> value){
            return RedisUtils.execute(new CallWithRedis<Object>() {
                @Override
                public Object call(Jedis jedis){
                    return jedis.eval(script,keys,value);
                }
            });
        }
    
        public static void close(){
            if(jedisPool != null){
                jedisPool.close();
            }
        }
    }

    二、单机下分布式锁

     redis是单线程的,所以指令都是原子操作,可实现分布式锁(乐观锁,类似于CAS自旋锁)。

    JVM锁synchronize和Lock是计数器实现,其中Lock用CAS自旋锁实现代码块的原子性来保证线程安全,

    redis实现分布式锁类似于CAS自旋锁实现,用setnx原子操作自旋实现代码块原子性来保证线程安全,但有几点需要注意。这篇博客描述的很详细

    自己实现的一个单机redis下分布式锁

    public class RedisWithLock implements Lock {
    
        private String key;
        private ThreadLocal<String> valueLocal = new ThreadLocal();
    
        /**
         * KEYS[1] == Arrays.asList(key).get(0)
         * ARGV[1] == Arrays.asList(value).get(0)
         */
        public static final String UNLOCK_EVAL =
                "if redis.call('get',KEYS[1]) == ARGV[1] then
    " +
                        "    return redis.call('del',KEYS[1])
    " +
                        "else
    " +
                        "    return 0
    " +
                        "    end";
    
        public RedisWithLock(String key){
            this.key = key;
        }
    
        @Override
        public void lock() {
            String value = new Random().nextLong() + "";
            valueLocal.set(value);
            for (;;){
                if ("OK".equals(RedisUtils.set(key,valueLocal.get(),SetParams.setParams().nx().ex(5)))){
                    String val = valueLocal.get();
                    //创建守护线程 unlock前刷新expire时间
                    Thread thread = new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                String lock = null;
                                for(;;){
                                    lock = RedisUtils.get(key);
                                    if(lock != null && lock.equals(val)){
                                        RedisUtils.expire(key,5);
                                        Thread.sleep(4000);
                                    }else{
                                        break;
                                    }
                                }
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                    thread.setDaemon(true);
                    thread.start();
                    break;
                }
            }
        }
    
        @Override
        public void unlock() {
            Object response = RedisUtils.eval(UNLOCK_EVAL, Arrays.asList(key), Arrays.asList(valueLocal.get()));
            if(Integer.valueOf(response.toString()) == 0){
                System.out.println("解锁失败");
            }
            valueLocal.remove();
        }
    
        @Override
        public void lockInterruptibly() throws InterruptedException {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public boolean tryLock() {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            throw new UnsupportedOperationException();
    
        }
    
        @Override
        public Condition newCondition() {
            throw new UnsupportedOperationException();
        }
    }
    
    //模拟服务A
    public class AppA {
        public static void main(String[] args){
            RedisWithLock lock = new RedisWithLock("lock");
            ExecutorService pool = Executors.newFixedThreadPool(2);
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    lock.lock();
                    try {
                        System.out.println("appA thread1 lock :"+DateUtils.format(System.currentTimeMillis()));
                        Thread.sleep(10000);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }finally {
                        lock.unlock();
                        System.out.println("appA thread1 unlock :"+DateUtils.format(System.currentTimeMillis()));
                    }
                }
            });
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    lock.lock();
                    try {
                        System.out.println("appA thread2 lock :"+DateUtils.format(System.currentTimeMillis()));
                        Thread.sleep(3000);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }finally {
                        lock.unlock();
                        System.out.println("appA thread2 unlock :"+DateUtils.format(System.currentTimeMillis()));
                    }
                }
            });
            pool.shutdown();
            while (!pool.isTerminated()){
    
            }
            RedisUtils.close();
        }
    }
    
    //模拟服务B
    public class AppB {
        public static void main(String[] args){
            RedisWithLock lock = new RedisWithLock("lock");
            ExecutorService pool = Executors.newFixedThreadPool(2);
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    lock.lock();
                    try {
                        System.out.println("appB thread1 lock :"+DateUtils.format(System.currentTimeMillis()));
                        Thread.sleep(3000);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }finally {
                        lock.unlock();
                        System.out.println("appB thread1 unlock :"+DateUtils.format(System.currentTimeMillis()));
                    }
                }
            });
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    lock.lock();
                    try {
                        System.out.println("appB thread2 lock :"+DateUtils.format(System.currentTimeMillis()));
                        Thread.sleep(3000);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }finally {
                        lock.unlock();
                        System.out.println("appB thread2 unlock :"+DateUtils.format(System.currentTimeMillis()));
                    }
                }
            });
            pool.shutdown();
            while (!pool.isTerminated()){
    
            }
            RedisUtils.close();
        }
    }

     三、集群下分布式锁——redlock(红锁)

    单机下实现的分布式锁,在集群下不是绝对线程安全的,例如一个客户端在主节点中申请成功一把锁,主节点还未来得及同步到从节点,主节点突然挂掉了(基本不会发生),从节点变成主节点,此时新主节点是没有锁的,当另一个客户端申请锁会成功。未解决这个问题Antirez发明了redlock算法:

    redlock算法:提供多个redis实例,实例之间相互独立,没有主从关系,加锁时过半redis实例set成功就认为加锁成功,释放锁时一样。

    redlock相比于单机的分布式锁,由于需要向多个节点进行读写,所以性能会低一些,使用哪种锁需要慎重考虑,redlock的不安全性 https://www.cnblogs.com/baichunyu/p/11631777.html

    参考《Redis深度历险》

  • 相关阅读:
    'telnet' 不是内部或外部命令,也不是可运行的程序 或批处理文件。
    Linux学习_009_VMware12.0 Pro 中安装 CentOS 6.8_超详解 + 高清大图
    Linux学习_008_Linux下的JDK和OpenJDK有什么具体的区别
    实战CentOS系统部署Hadoop集群服务
    开源多线程性能测试工具-sysbench
    Hadoop集群中Hbase的介绍、安装、使用
    Docker基础技术:Linux Namespace(上)
    带你走进rsync的世界
    5分钟教你Windows 10中将“运行”固定到开始菜单
    使用 Github Pages 发布你的项目文档
  • 原文地址:https://www.cnblogs.com/wqff-biubiu/p/12275728.html
Copyright © 2011-2022 走看看