• Redis实现分布式锁


    要介绍分布式锁,首先要提到与分布式锁相对应的是线程锁、进程锁。

    线程锁:主要用来给方法、代码块加锁。当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该代码段。线程锁只在同一JVM中有效果,因为线程锁的实现在根本上是依靠线程之间共享内存实现的,比如synchronized是共享对象头,显示锁Lock是共享某个变量(state)。

    进程锁:为了控制同一操作系统中多个进程访问某个共享资源,因为进程具有独立性,各个进程无法访问其他进程的资源,因此无法通过synchronized等线程锁实现进程锁。

    分布式锁:当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问。

    1. 互斥性,同一时刻,智能有一个客户端持有锁。
    2. 防止死锁发生,如果持有锁的客户端崩溃没有主动释放锁,也要保证锁可以正常释放及其他客户端可以正常加锁。
    3. 加锁和释放锁必须是同一个客户端。
    4. 容错性,只有redis还有节点存活,就可以进行正常的加锁解锁操作。

    基本上避免了以上几种错误方式之外,就是正确的方式了。要满足以下几个条件:

    命令必须保证互斥

    设置的key必须要有过期时间,防止崩溃时锁无法释放

    value使用唯一id标志每个客户端,保证只有锁的持有者才可以释放锁

    加锁直接使用set命令同时设置唯一id和过期时间;其中解锁稍微复杂些,加锁之后可以返回唯一id,标志此锁是该客户端锁拥有;释放锁时要先判断拥有者是否是自己,然后删除,这个需要redis的lua脚本保证两个命令的原子性执行。

    1. @Slf4j
    2. public class RedisDistributedLock {
    3. private static final String LOCK_SUCCESS = "OK";
    4. private static final Long RELEASE_SUCCESS = 1L;
    5. private static final String SET_IF_NOT_EXIST = "NX";
    6. private static final String SET_WITH_EXPIRE_TIME = "PX";
    7. // 锁的超时时间
    8. private static int EXPIRE_TIME = 5 * 1000;
    9. // 锁等待时间
    10. private static int WAIT_TIME = 1 * 1000;
    11. private Jedis jedis;
    12. private String key;
    13. public RedisDistributedLock(Jedis jedis, String key) {
    14. this.jedis = jedis;
    15. this.key = key;
    16. }
    17. // 不断尝试加锁
    18. public String lock() {
    19. try {
    20. // 超过等待时间,加锁失败
    21. long waitEnd = System.currentTimeMillis() + WAIT_TIME;
    22. String value = UUID.randomUUID().toString();
    23. while (System.currentTimeMillis() < waitEnd) {
    24. String result = jedis.set(key, value, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, EXPIRE_TIME);
    25. if (LOCK_SUCCESS.equals(result)) {
    26. return value;
    27. }
    28. try {
    29. Thread.sleep(10);
    30. } catch (InterruptedException e) {
    31. Thread.currentThread().interrupt();
    32. }
    33. }
    34. } catch (Exception ex) {
    35. log.error("lock error", ex);
    36. }
    37. return null;
    38. }
    39. public boolean release(String value) {
    40. if (value == null) {
    41. return false;
    42. }
    43. // 判断key存在并且删除key必须是一个原子操作
    44. // 且谁拥有锁,谁释放
    45. String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    46. Object result = new Object();
    47. try {
    48. result = jedis.eval(script, Collections.singletonList(key),
    49. Collections.singletonList(value));
    50. if (RELEASE_SUCCESS.equals(result)) {
    51. log.info("release lock success, value:{}", value);
    52. return true;
    53. }
    54. } catch (Exception e) {
    55. log.error("release lock error", e);
    56. } finally {
    57. if (jedis != null) {
    58. jedis.close();
    59. }
    60. }
    61. log.info("release lock failed, value:{}, result:{}", value, result);
    62. return false;
    63. }

     

  • 相关阅读:
    curl命令介绍
    题目:一个整数,它加上100 后是一个完全平方数, 再加上168 又是一个完全平方数,请问该数是多少?
    【BurpSuite】插件开发学习之J2EEScan(下)-主动扫描(11-20)
    AlertManager解析:构建高效告警系统
    Python图像处理笔记
    海康威视热成像实时测温java - 23版
    设计模式-原则篇-01.开闭原则
    基于Java和MySql的产业信息管理系统的设计与实现 毕业设计-附源码260839
    PAT甲级:1049 Counting Ones
    1004. 最大连续1的个数 III ●●
  • 原文地址:https://blog.csdn.net/weixin_53150299/article/details/134006409