Java分布式锁由浅入深介绍

Java分布式锁由浅入深介绍

什么是分布式锁

分布式锁是一种通过共享锁来保证分布式环境下多进程、多线程之间数据同步的技术。常用的锁算法有互斥锁、读写锁、乐观锁、悲观锁等。

基于Zookeeper的分布式锁

Zookeeper是一种分布式协同管理工具,提供了一种基于节点的会话机制,这种机制可以通过锁节点来控制多个进程的协调。Zookeeper主要有以下特点:

  • 一致性:所有客户端在同一时间看到同样的节点视图
  • 分区容错性:容忍网络分区故障
  • 原子性:一次操作要么成功要么失败

以下是基于Zookeeper实现的一个简单分布式锁的示例

public class ZookeeperLock implements Lock{
    private ZooKeeper zooKeeper;
    private String lockPath;
    private String lockName;
    private String currentNodeName;
    private ThreadLocal<AtomicInteger> reentryCount = new ThreadLocal<>();
    private static final String SEPARATOR = "/";

    public ZookeeperLock(String connectString, int sessionTimeout, String lockPath, String lockName) {
        this.zooKeeper = new ZooKeeper(connectString, sessionTimeout, event -> {});
        this.lockPath = SEPARATOR + lockPath;
        this.lockName = SEPARATOR + lockName;
        // 如果lockPath不存在就创建一个
        try{
            if(zooKeeper.exists(this.lockPath, false) == null){
                zooKeeper.create(this.lockPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void lock() {
        if(currentNodeName != null){
            // 如果已经拥有锁,则重入次数+1,返回
            reentryCount.get().incrementAndGet();
            return;
        }
        try{
            // 创建一个临时节点
            String nodeName = zooKeeper.create(lockPath + lockName, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            currentNodeName = nodeName.substring(lockPath.length());
            // 如果获取锁失败,注册监听器
            while(true){
                List<String> children = zooKeeper.getChildren(lockPath, false);
                SortedSet<String> sortedNodeNames = new TreeSet<>();
                for(String child : children){
                    sortedNodeNames.add(lockPath + child);
                }
                String firstNodeName = sortedNodeNames.first();
                if(lockPath + currentNodeName.equals(firstNodeName)){
                    return;
                }
                CountDownLatch countDownLatch = new CountDownLatch(1);
                String previousNodeName = null;
                for(String nodeName : sortedNodeNames){
                    if((lockPath + currentNodeName).equals(nodeName)){
                        break;
                    }
                    previousNodeName = nodeName.substring(lockPath.length());
                }
                if(previousNodeName != null){
                    echoNodeCreatedWatcher(previousNodeName, countDownLatch);
                    countDownLatch.await();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 注册监听器
     */
    private void echoNodeCreatedWatcher(String nodeName, CountDownLatch countDownLatch){
        try{
            zooKeeper.exists(lockPath + nodeName, event -> {
                if(event.getType() == NodeDeleted){
                    countDownLatch.countDown();
                }
            });
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void unlock() {
        if(currentNodeName == null){
            throw new RuntimeException("锁还未被获取");
        }
        int count = reentryCount.get().decrementAndGet();
        if(count > 0){
            return;
        }
        try{
            zooKeeper.delete(lockPath + currentNodeName, -1);
            currentNodeName = null;
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    ...
}

基于Redis的分布式锁

Redis也是一种分布式缓存中间件,提供了一种可重入的分布式锁,实现比Zookeeper简单,且性能也更高。

以下是基于Redis实现的一个简单分布式锁的示例

public class RedisLock implements Lock{
    private static final String LOCK_PREFIX = "redis_lock:";

    private Jedis jedis;
    private String lockKey;
    private int lockExpireMills;
    private String lockValue;
    private ThreadLocal<Integer> reentryCount;

    public RedisLock(Jedis jedis, String lockKey, int lockExpireMills){
        this.jedis = jedis;
        this.lockKey = LOCK_PREFIX + lockKey;
        this.lockExpireMills = lockExpireMills;
        this.reentryCount = ThreadLocal.withInitial(() -> 0);
    }

    @Override
    public void lock() {
        if(reentryCount.get() > 0){
            reentryCount.set(reentryCount.get() + 1);
            return;
        }
        lockValue = UUID.randomUUID().toString();
        String result = jedis.set(lockKey, lockValue, "NX", "PX", lockExpireMills);
        if(!"OK".equals(result)){
            throw new IllegalStateException("获取锁失败");
        }else{
            reentryCount.set(1);
        }
    }

    ...

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java分布式锁由浅入深介绍 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Java Lambda表达式常用的函数式接口

    Java Lambda表达式是函数式编程的核心特性之一,其中,函数式接口是Lambda表达式的基础。函数式接口是指仅包含一个抽象方法的接口,用来表示函数的签名。Java中已经预定义了很多常用的函数式接口,包括Consumer、Supplier、Function、Predicate等。下面我们逐一来介绍这些函数式接口,并提供几个示例说明。 Consumer C…

    Java 2023年5月26日
    00
  • Mysql json类型字段Java+Mybatis数据字典功能的实践方式

    Mysql json类型字段Java+Mybatis数据字典功能的实践方式概述 Mysql支持json类型数据,在应用程序开发中,经常需要将json类型数据存储到数据库中。考虑到数据字典的实现方式,可以将字典数据以json的方式存储到Mysql数据库表中,Java+Mybatis数据字典功能是通过将json类型的数据解析出来,然后在应用程序中使用这些数据。 …

    Java 2023年5月20日
    00
  • java list用法示例详解

    Java List用法示例详解 概述 Java中List是一个用于存储一组有序元素的接口,它是java.util包中的一个接口。List接口的实现类有ArrayList、LinkedList等,它们都是用于存储为一组有序元素的集合。本文将对Java中List的用法进行详细的介绍。 创建List 创建List的方法如下,其中“E”代表元素的类型。 List&l…

    Java 2023年5月26日
    00
  • Maven pom.xml与settings.xml详解

    Maven是一个流行的Java构建工具,是基于项目对象模型(Project Object Model, POM)进行构建的。POM是一个XML文件,描述了项目的依赖关系、构建环境、代码目录、打包、部署等信息。POM通过继承机制实现了依赖管理和构建配置的复用,是Maven强大的特性之一。而settings.xml是Maven的配置文件,它包含了Maven的配置…

    Java 2023年5月20日
    00
  • 什么是方法区?

    以下是关于 Java 方法区的详细讲解: 什么是方法区? Java 方法区是一种用于存储已加载类信息、常量、静态变量、即时编译器编译后的代码数据的内存区域。方法区是线程共享的,的大小可以通过 -XX:MaxMetaspaceSize 参数进行设置。 Java 方法区使用攻略 使用 Java 方法区,需要注意以下几点: 在程序开发中,需要合理内存,避免出现内存…

    Java 2023年5月12日
    00
  • 值得收藏的SpringBoot 实用的小技巧

    值得收藏的SpringBoot实用的小技巧 在SpringBoot的开发过程中,有一些实用的小技巧可以提高开发效率,降低代码量和阅读难度。下面列举了一些值得收藏的小技巧。 1. 使用lombok简化实体类的编写 在实体类中,我们通常需要定义常量、属性、getter/setter、toString等方法,这些方法都是重复的代码,使用lombok可以自动生成这些…

    Java 2023年5月15日
    00
  • SpringBoot整合Shiro实现权限控制的代码实现

    下面我将为您详细讲解“SpringBoot整合Shiro实现权限控制的代码实现”的完整攻略,主要分为以下几个步骤: 1. 引入相关依赖 在 pom.xml 中添加以下依赖: <dependencies> <!– SpringBoot相关依赖 –> <dependency> <groupId>org.spri…

    Java 2023年5月20日
    00
  • Java Thread 类和Runnable 接口详解

    Java Thread 类和 Runnable 接口详解 概述 Java 中的线程是并发编程的核心内容,可以同时执行多个任务。Java 提供了两种线程的方式:继承Thread类和实现Runnable接口。 Thread 类 Thread类是Java中的一个顶级类,使用它可以快速地创建并发程序。Java 程序中的 main() 方法也是一个线程,使用Threa…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部