zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

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

Redis分布式分布式 简单 自己
2023-09-27 14:28:22 时间

自己写了个简单的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();
        }
        
    }
    
}