redis的分布式锁有什么作用?redis分布式锁如何实现?

Redis 不仅可以将数据完全保存在内存中,还可以通过磁盘实现数据的持久存储,所以redis是一个基于内存实现的键值型非关系数据库,那redis的分布式锁有什么作用?下面来我们就来给大家讲解一下。

redis的分布式锁有什么作用.jpg

在分布式系统中,当不同进程或线程一起访问共享资源时,会造成资源争抢,如果不加以控制的话,就会引发程序错乱。此时使用分布式锁能够非常有效的解决这个问题,它采用了一种互斥机制来防止线程或进程间相互干扰,从而保证了数据的一致性。

redis分布式锁如何实现?

分布式锁实现的关键是在分布式的应用服务器外,搭建一个存储服务器,存储锁信息,这时候我们很容易就想到了Redis。首先我们要搭建一个Redis服务器,用Redis服务器来存储锁信息。

在实现的时候要注意的几个关键点:

1、锁信息必须是会过期超时的,不能让一个线程长期占有一个锁而导致死锁;

2、同一时刻只能有一个线程获取到锁。

几个要用到的redis命令:

setnx(key, value):“set if not exits”,若该key-value不存在,则成功加入缓存并且返回1,否则返回0。

get(key):获得key对应的value值,若不存在则返回nil。

getset(key, value):先获取key对应的value值,若不存在则返回nil,然后将旧的value更新为新的value。

expire(key, seconds):设置key-value的有效期为seconds秒。

看一下流程图:

redis的分布式锁有什么作用?redis分布式锁如何实现?.jpg

在这个流程下,不会导致死锁。

我采用Jedis作为Redis客户端的api,下面来看一下具体实现的代码。

(1)首先要创建一个Redis连接池。

public class RedisPool
{
    private static JedisPool pool; //jedis连接池
    private static int maxTotal = 20; //最大连接数
    private static int maxIdle = 10; //最大空闲连接数
    private static int minIdle = 5; //最小空闲连接数
    private static boolean testOnBorrow = true; //在取连接时测试连接的可用性
    private static boolean testOnReturn = false; //再还连接时不测试连接的可用性
    static
    {
        initPool(); //初始化连接池
    }
    public static Jedis getJedis()
    {
        return pool.getResource();
    }
    public static void close(Jedis jedis)
    {
        jedis.close();
    }
    private static void initPool()
    {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(maxTotal);
        config.setMaxIdle(maxIdle);
        config.setMinIdle(minIdle);
        config.setTestOnBorrow(testOnBorrow);
        config.setTestOnReturn(testOnReturn);
        config.setBlockWhenExhausted(true);
        pool = new JedisPool(config, "127.0.0.1", 6379, 5000, "liqiyao");
    }
}

(2)对Jedis的api进行封装,封装一些实现分布式锁需要用到的操作。

public class RedisPoolUtil
{
    private RedisPoolUtil()
    {}
    private static RedisPool redisPool;
    public static String get(String key)
    {
        Jedis jedis = null;
        String result = null;
        try
        {
            jedis = RedisPool.getJedis();
            result = jedis.get(key);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (jedis != null)
            {
                jedis.close();
            }
            return result;
        }
    }
    public static Long setnx(String key, String value)
    {
        Jedis jedis = null;
        Long result = null;
        try
        {
            jedis = RedisPool.getJedis();
            result = jedis.setnx(key, value);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (jedis != null)
            {
                jedis.close();
            }
            return result;
        }
    }
    public static String getSet(String key, String value)
    {
        Jedis jedis = null;
        String result = null;
        try
        {
            jedis = RedisPool.getJedis();
            result = jedis.getSet(key, value);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (jedis != null)
            {
                jedis.close();
            }
            return result;
        }
    }
    public static Long expire(String key, int seconds)
    {
        Jedis jedis = null;
        Long result = null;
        try
        {
            jedis = RedisPool.getJedis();
            result = jedis.expire(key, seconds);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (jedis != null)
            {
                jedis.close();
            }
            return result;
        }
    }
    public static Long del(String key)
    {
        Jedis jedis = null;
        Long result = null;
        try
        {
            jedis = RedisPool.getJedis();
            result = jedis.del(key);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (jedis != null)
            {
                jedis.close();
            }
            return result;
        }
    }
}

(3)分布式锁工具类

public class DistributedLockUtil
{
    private DistributedLockUtil()
    {}
    public static boolean lock(String lockName)
    { //lockName可以为共享变量名,也可以为方法名,主要是用于模拟锁信息
        System.out.println(Thread.currentThread() + "开始尝试加锁!");
        Long result = RedisPoolUtil.setnx(lockName, String.valueOf(System.currentTimeMillis() + 5000));
        if (result != null && result.intValue() == 1)
        {
            System.out.println(Thread.currentThread() + "加锁成功!");
            RedisPoolUtil.expire(lockName, 5);
            System.out.println(Thread.currentThread() + "执行业务逻辑!");
            RedisPoolUtil.del(lockName);
            return true;
        }
        else
        {
            String lockValueA = RedisPoolUtil.get(lockName);
            if (lockValueA != null && Long.parseLong(lockValueA) >= System.currentTimeMillis())
            {
                String lockValueB = RedisPoolUtil.getSet(lockName, String.valueOf(System.currentTimeMillis() + 5000));
                if (lockValueB == null || lockValueB.equals(lockValueA))
                {
                    System.out.println(Thread.currentThread() + "加锁成功!");
                    RedisPoolUtil.expire(lockName, 5);
                    System.out.println(Thread.currentThread() + "执行业务逻辑!");
                    RedisPoolUtil.del(lockName);
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }
    }
}

这样我们就能实现redis分布式锁,使用redis分布式锁能够有效避免程序错乱,并且能够保证数据的一致性。最后大家如果想要了解更多java架构师知识,敬请关注奇Q工具网。

推荐阅读:

java如何一步写四种加法算式?java怎么实现四则运算?

java经典算法面试题有哪些?java经典算法面试题总结

java培训课程有什么?java培训课程好学吗?