• 自己写了个简单的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();
            }
            
        }
        
    }
  • 相关阅读:
    Dynamics CRM中的地址知多D?
    配置基于服务器认证的Dynamics 365 Customer Engagement和SharePoint Online集成
    Dynamics 365利用Web API对视图进行查询
    Dynamics 365中的公告(Post)分析
    嵌入Canvas App到Dynamics 365 Customer Engagement(Model-Driven App)中,创造更多可能!
    Dynamics 365中开发和注册插件介绍
    Dynamics 365客户端编程示例:获取当前用户的信息,表单级通知/提示,表单OnLoad事件执行代码
    Dynamics 365客户端编程示例:两个选项集字段的联动
    利用ExecuteMultipleRequest来批量导入数据,成功的成功失败的失败,并生成导入结果文件
    Dynamics 365中使用计算字段自动编号字段实时工作流自动生成分组编码加流水号的自动编号字段值
  • 原文地址:https://www.cnblogs.com/libin6505/p/11284401.html
Copyright © 2020-2023  润新知