• 自己写了个简单的redis分布式锁【我】


    自己写了个简单的redis分布式锁

    【注意:此锁需要在每次使用前都创建对象,也就是要在线程内每次都创建对象后使用】

    package redis;
    
    import java.util.Collections;
    import java.util.Random;
    import java.util.UUID;
    
    import org.redisson.Redisson;
    import org.redisson.api.RLock;
    import org.redisson.api.RedissonClient;
    import org.redisson.config.Config;
    
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    
    public class RedisLock2 {
    
        JedisPool jedisPool;
        private String key;
        private String value;
        //默认锁超时时间5秒
        private Long timeout = 5L;
        //加锁成功的时间起点
        private Long startTime;
        
        /**
         * 使用默认超时时间
         * @param JedisPool
         * @param key
         */
        public RedisLock2(JedisPool JedisPool,String key) {
            super();
            this.jedisPool = JedisPool;
            this.key = key;
            value = System.currentTimeMillis()+"";
        }
        
        /**
         * 单独设置超时时间
         * @param JedisPool
         * @param key  锁的名称
         * @param timeout 锁超时时间
         */
        public RedisLock2(JedisPool JedisPool,String key,Long timeout) {
            super();
            this.jedisPool = JedisPool;
            this.key = key;
            this.timeout = timeout;
            value = UUID.randomUUID().toString();
    //        System.out.println("创建锁时的value:"+value);
        }
    
        /**
         * 单次加锁,需要判断返回值【适用于获取不到锁就返回的业务场景】
         * @return true:加锁成功; false:加锁失败
         */
        public boolean lock() {
            Jedis jedis = jedisPool.getResource();
            boolean ok = jedis.set(key,value, "nx", "ex", timeout)!=null;
            jedis.close();
            if (!ok) {
                //加锁失败
                return false;
            }
            //加锁成功
            startTime = System.currentTimeMillis();
            return true;
        }
        
        /**
         * 阻塞重试加锁,默认时间间隔10毫秒
         */
        public void lockRetry() {
            lockRetry(null);
        } 
        
        /**
         * 阻塞重试加锁,直到加锁成功【适用于一定要执行的业务】
         * @param interval 重试时间间隔毫秒值
         */
        public void lockRetry(Long interval) {
            if (interval==null) {
                //默认10毫秒重试一次
                interval = 10L;
            }else if(interval==0) {
                //如果传值为0,则取10以内随机值
                interval =  (long) (new Random().nextInt(10) + 1);
            }
            int num = 0;
            while (true) {
                boolean b =  lock();
                num++;
                if (b) {
                    System.out.println("加锁  "+num+" 次后成功");
                    break;
                }
                try {
                    //休息10毫秒后重试
                    Thread.sleep(interval);
                    System.out.println("有并发,休息:"+interval+"毫秒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } 
        
        /**
         * 解锁【首先尝试用lua方式,如果redis的版本不支持lua,用普通方式】
         * @return
         */
        public String unlock() {
            //解锁返回消息(非正常解锁时会打印,还可以在业务中解锁时判断此返回值)
            String msg = "";
            //加锁到解锁消耗时间
            long timeConsume = System.currentTimeMillis() - startTime;
            //超时直接返回
            if (timeConsume > timeout * 1000) {
                System.out.println("出现超时解锁-----------key:" + key + ",耗时毫秒:" + timeConsume);
    //            return false;
                msg = "出现超时解锁--key:" + key + ",耗时毫秒:" + timeConsume;
                return msg;
            }
            //这里是为了避免超时后,释放掉其他线程的同名锁
            String luaScript = "if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else  return 0 end";
            Jedis jedis = jedisPool.getResource();
            //执行lua脚本返回值
            Object evalRtn = 0;
            try {
                evalRtn = jedis.eval(luaScript, Collections.singletonList(key), Collections.singletonList(value));
    //            System.out.println("lua解锁返回值:"+evalRtn);
            } catch (Exception e) {
                e.printStackTrace();
                //如果当前redis不支持lua脚本,用下面方法,但是下面的代码不是原子操作,可能会有并发问题,这里忽略
                String s = jedis.get(key);
                if (value.equals(s)) {
                    //释放锁
                    jedis.del(key);
    //                return true;
                    msg = "解锁成功";
                    return msg;
                }
            }finally {
                jedis.close();
            }
            if ((Long) evalRtn == 1) {
    //             return true;
                msg = "解锁成功";
                return msg;
            }
            System.out.println("出现其他异常解锁失败---------key:" + key);
    //        return false;
            msg = "出现其他异常解锁失败--key:" + key;
            return msg;
        }
        
        //简单加锁测试
        public static void main1(String[] args) throws InterruptedException {
            JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);
            //设置超时时间100秒
            RedisLock2 redisLock = new RedisLock2(jedisPool,"lock1",100L);
            //加锁
            boolean lock = redisLock.lock();
            //获取到锁才执行,获取不到不执行
            if(lock) {
                //执行业务逻辑(要保证业务逻辑执行时间小于锁超时时间)
                System.out.println("我获取到锁了");
    //            Thread.sleep(2000);
            }
            System.out.println(redisLock.unlock());
        }
        
        
        //开启多线程往redis中设置值,保证不覆盖(用单次锁在业务逻辑中循环阻塞)
        public static void main2(String[] args) {
            JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);
            //原来是使用网上的一种锁
    //        DistributedLock lock = new DistributedLock(jedisPool);
            
            for (int i = 0; i < 3; i++) {
    //            final int k = i;
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int j = 0; j < 10; j++) {
                            int k = j;
                            // 连接本地的 Redis 服务
    //                        Jedis jedis = new Jedis("localhost");
    //                        String code = null;
                            RedisLock2 myLock = null;
                            try {
                                //加分布式锁
    //                            code = lock.lock("mylock");
                                myLock = new RedisLock2(jedisPool,"lock1",10L);
                                while (true) {
                                    //不断获取锁
                                    boolean lock = myLock.lock();
                                    if (lock) {
                                        //如果获取到则执行
                                        // 从连接池中获取一个jedis对象
                                        Jedis jedis = jedisPool.getResource();
                                        if (!jedis.exists("a" + k)) {
                                            jedis.set("a" + k, Thread.currentThread().getName());
                                            jedis.expire("a" + k, 60);
                                            System.out.println(System.currentTimeMillis() + "--" + Thread.currentThread().getName()
                                                    + "--key:" + ("a" + k) + "不存在,设置值为: " + Thread.currentThread().getName());
                                            try {
                                                // Thread.sleep((long) (Math.random()*1000));
                                            } catch (Exception e) {
                                                e.printStackTrace();
                                            }
                                        } else {
                                            System.out.println(Thread.currentThread().getName() + "--key:" + ("a" + k) + "存在,值为: "
                                                    + jedis.get("a" + k));
                                        }
                                        jedis.close();
                                        break;//跳出循环
                                    }
                                    
                                }
                                
                            } finally {
                                //释放分布式锁
    //                            lock.unLock("mylock",code);
                                //执行完解锁
                                myLock.unlock();
                            }
                        }
                    }
                }).start();
            }
        }
        
        //开启多线程往redis中设置值,保证不覆盖(用阻塞锁)
        public static void main(String[] args) {
            //原来是使用rdissen锁
            //获取redisson
    //        Config config = new Config();
    //        config.useSingleServer().setAddress("redis://localhost:6379");
    //        RedissonClient redisson = Redisson.create(config);
    //        //获取锁
    //        RLock lock = redisson.getLock("mylock");
            
             // 创建连接池对象
            JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);
    
            for (int i = 0; i < 3; i++) {
    //            final int k = i;
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int j = 0; j < 10; j++) {
                            int k = j;
                            // 连接本地的 Redis 服务
    //                        Jedis jedis = new Jedis("localhost");
                            RedisLock2 myLock = null;
                            try {
                                //加redisson分布式锁
    //                            lock.lock();
                                //用我们自己写的重试锁【自定义锁非可重入锁,需要在线程中每次使用时都创建一个锁对象,多线程中只要名称相同就认为是同一个锁】
                                myLock = new RedisLock2(jedisPool,"lock1",10L);
                                //开启阻塞锁
                                myLock.lockRetry(3L);
                                //执行业务逻辑【因为用阻塞锁,无需判断返回值】
                                // 从连接池中获取一个jedis对象
                                Jedis jedis = jedisPool.getResource();
                                if (!jedis.exists("a" + k)) {
                                    jedis.set("a" + k, Thread.currentThread().getName());
                                    jedis.expire("a" + k, 60);
    //                                System.out.println(System.currentTimeMillis() + "--" + Thread.currentThread().getName()
    //                                        + "--key:" + ("a" + k) + "不存在,设置值为: " + Thread.currentThread().getName());
                                    try {
                                        // Thread.sleep((long) (Math.random()*1000));
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                } else {
    //                                System.out.println(Thread.currentThread().getName() + "--key:" + ("a" + k) + "存在,值为: "
    //                                        + jedis.get("a" + k));
                                }
                                jedis.close();
                            } finally {
                                //释放redisson分布式锁
    //                            lock.unlock();
                                //用我自己定义的锁
                                myLock.unlock();
                            }
                        }
                    }
                }).start();
            }
            
        }
        
    }
  • 相关阅读:
    [转]VS 2008 新特性一览
    [转]WCF开发框架形成之旅WCF的几种寄宿方式
    [转]失业的程序员(五):商战之前
    [ 转]Android折叠列表 ExpandableList
    [转]extjs入门——开始
    [转]Jquery操作select
    Ext JS 下载 及 是否收费
    [转]WCF服务的批量寄宿
    [转]Android高手进阶教程(十七)之Android中Intent传递对象的两种方法(Serializable,Parcelable)!
    mat之三PermGen Metaspace永久代内存分析
  • 原文地址:https://www.cnblogs.com/libin6505/p/11284401.html
Copyright © 2020-2023  润新知