• 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深度历险》

  • 相关阅读:
    循环语句的基本使用
    创建一个可拖动的dom元素。
    JavaScript中的callee,caller,call,apply的使用
    两个数组去重的方法。
    利用setTimeout建立能捕捉鼠标多次点击和鼠标长按的事件处理程序。
    document.getElementByClassName()的使用和兼容老浏览器。
    jQuery .data()方法的运用。
    javascript对象的深拷贝。
    未来、
    linux上机作业
  • 原文地址:https://www.cnblogs.com/wqff-biubiu/p/12275728.html
Copyright © 2020-2023  润新知