这期内容当中小编将会给大家带来有关redis实现分布式锁的原理,以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
分布式锁,是控制分布式系统之间同步访问共享资源的一种方式。
在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。
使用setnx、getset、expire、del这4个redis命令实现
setnx 是『SET if Not eXists』(如果不存在,则 SET)的简写。 命令格式:SETNX key value;使用:只在键 key 不存在的情况下,将键 key 的值设置为 value 。若键 key 已经存在, 则 SETNX 命令不做任何动作。返回值:命令在设置成功时返回 1 ,设置失败时返回 0 。
getset 命令格式:GETSET key value,将键 key 的值设为 value ,并返回键 key 在被设置之前的旧的value。返回值:如果键 key 没有旧值, 也即是说, 键 key 在被设置之前并不存在, 那么命令返回 nil 。当键 key 存在但不是字符串类型时,命令返回一个错误。
expire 命令格式:EXPIRE key seconds,使用:为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。返回值:设置成功返回 1 。 当 key 不存在或者不能为 key 设置生存时间时(比如在低于 2.1.3 版本的 Redis 中你尝试更新 key 的生存时间),返回 0 。
del 命令格式:DEL key [key …],使用:删除给定的一个或多个 key ,不存在的 key 会被忽略。返回值:被删除 key 的数量。
Redis实现分布式锁的原理:
1.通过setnx(lock_timeout)实现,如果设置了锁返回1, 已经有值没有设置成功返回0
2.死锁问题:通过实践来判断是否过期,如果已经过期,获取到过期时间get(lockKey),然后getset(lock_timeout)判断是否和get相同,相同则证明已经加锁成功,因为可能导致多线程同时执行getset(lock_timeout)方法,这可能导致多线程都只需getset后,对于判断加锁成功的线程, 再加expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS)过期时间,防止多个线程同时叠加时间,导致锁时效时间翻倍
代码:
/** * @author yaoxin * @date 2018/8/13下午5:04 */ public class RedisLockTest { public static final String url = "jdbc:mysql://127.0.0.1:3306/ly?characterEncoding=UTF-8"; public static final String name = "com.mysql.jdbc.Driver"; public static final String user = "root"; public static final String password = ""; public static void main(String[] args) { Integer count = 50; while (count > 0) { count--; new Thread(new Runnable() { @Override public void run() { Jedis jedis = new Jedis("127.0.0.1", 6379); jedis.auth("1234"); String lock = lock(jedis); if (lock != null) { Statement statement = null; Connection conn = null; ResultSet resultSet = null; try { Class.forName(name);// 指定连接类型 conn = DriverManager.getConnection(url, user, password);// 获取连接 statement = conn.createStatement();// 准备执行语句 String querySql = "SELECT id,name,count FROM production WHERE id=2"; resultSet = statement.executeQuery(querySql); int count = 0; while (resultSet.next()) { System.out.println(Thread.currentThread().getName() + "抢到了锁 id: " + resultSet.getString("id") + " name: " + resultSet.getString("name") + " count: " + resultSet.getString("count")); count = Integer.valueOf(resultSet.getString("count")); } String updateSql = "UPDATE production SET count=" + (count - 1) + " WHERE id=2"; int rows = statement.executeUpdate(updateSql); if (rows > 0) { System.out.println("更新成功" + Thread.currentThread().getName() + " 库存剩余:" + (count - 1)); System.out.println(Thread.currentThread().getName() + " === > >开始解锁"); boolean unlock = unlock(jedis, lock); if (unlock) System.out.println(Thread.currentThread().getName() + " === > >解锁成功"); } else { System.out.println("更新失败" + Thread.currentThread().getName()); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (conn != null) conn.close(); if (statement != null) statement.close(); if (resultSet != null) resultSet.close(); } catch (Exception e) { e.printStackTrace(); } } } } }, "线程" + count).start(); } } public static String lock(Jedis jedis) { try { while (true) { String lockTime = Long.valueOf(jedis.time().get(0)) + 5 + ""; if (jedis.setnx("lock", lockTime) == 1) { jedis.expire("lock", 5); return lockTime; } String lock = jedis.get("lock"); if (!StringUtils.isEmpty(lock) && Long.valueOf(lock) < Long.valueOf(jedis.time().get(0))) { String oldLockTime = jedis.getSet("lock", lockTime); if (!StringUtils.isEmpty(oldLockTime) && oldLockTime.equals(lock)) { return lockTime; } } Thread.sleep(100); } } catch (Exception e) { e.printStackTrace(); } return null; } public static boolean unlock(Jedis jedis, String lockTag) { if (lockTag.equals(jedis.get("lock"))) { jedis.del("lock"); return true; } return false; } }
运行结果如下图:
上述就是小编为大家分享的redis实现分布式锁的原理,如果您也有类似的疑惑,不妨参照上述方法进行尝试。如果想了解更多相关内容,请关注亿速云行业资讯。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。