Redis 不仅可以将数据完全保存在内存中,还可以通过磁盘实现数据的持久存储,所以redis是一个基于内存实现的键值型非关系数据库,那redis的分布式锁有什么作用?下面来我们就来给大家讲解一下。
在分布式系统中,当不同进程或线程一起访问共享资源时,会造成资源争抢,如果不加以控制的话,就会引发程序错乱。此时使用分布式锁能够非常有效的解决这个问题,它采用了一种互斥机制来防止线程或进程间相互干扰,从而保证了数据的一致性。
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秒。
看一下流程图:
在这个流程下,不会导致死锁。
我采用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工具网。
推荐阅读: