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封装数组之改进为泛型数组操作详解

    Java封装数组之改进为泛型数组操作详解 在Java程序开发中,经常会使用数组来存储和处理数据,但是传统的数组存储方式存在类型不安全、代码冗长等问题,为了解决这些问题,Java提供了泛型数组,即封装数组。本文将详细介绍Java封装数组的概念,封装原理以及如何改进为泛型数组的操作步骤和技巧。 一、概念 Java封装数组是指在类中定义数组变量,封装了数组的属性和…

    Java 2023年5月26日
    00
  • IDEA实现 springmvc的简单注册登录功能的示例代码

    以下是“IDEA实现 springmvc的简单注册登录功能的示例代码”的完整攻略: 创建 Maven Web 项目 首先,在 IDEA 中创建一个 Maven Web 项目,选择 Spring MVC。 配置 pom.xml 文件 在 pom.xml 文件中添加 Spring 相关的依赖,包括 spring-webmvc、spring-orm、spring-…

    Java 2023年5月16日
    00
  • Java Spring的使用注解开发详解

    Java Spring的使用注解开发详解 Java Spring是一个开源框架,它帮助Java开发人员开发企业级应用程序。Spring框架有多种模块,其中最流行的是Spring Core,它是Spring框架的核心部分,提供了依赖注入(DI)和面向切面编程(AOP)等重要功能。本文将详细讲解如何使用注解开发Java Spring应用程序。 环境准备 在开始使…

    Java 2023年5月19日
    00
  • Asp.net FileUpload+Image制作头像效果示例代码

    我们来详细讲解一下“ASP.NET FileUpload+Image制作头像效果示例代码”的完整攻略。 概述 首先,我们需要了解一些基本的概念。在 ASP.NET 中,我们可以使用 FileUpload 控件来接收用户上传的文件,使用 Image 控件来展示上传的图片。一般来说,用户上传头像时,我们需要对其进行剪裁、压缩等操作,以获得更好的用户体验。 第一步…

    Java 2023年5月19日
    00
  • Struts2学习教程之Action类如何访问WEB资源

    为了让Action类能够访问WEB资源,需要进行以下几个步骤: 1. 在struts.xml中进行配置 在struts.xml中需要配置一个<constant>元素,设置resourceBase属性为需要访问的WEB资源的路径。 示例代码: <constant name="struts.convention.result.path…

    Java 2023年5月20日
    00
  • Java栈的三种实现方式(完整版)

    Java栈的三种实现方式 什么是栈 栈(Stack)是一种常见的数据结构,它的特点是后进先出(LIFO,Last In First Out),就是存入栈的元素的顺序是先后顺序,最后存入的元素最先取出。栈只允许在栈顶进行插入和删除操作。 在程序中,栈常用于实现递归、函数调用和表达式求值等相关操作。 栈的实现方式 Java语言中,栈的实现通常有以下三种方式: 继…

    Java 2023年5月18日
    00
  • IDEA 中 maven 的 Lifecycle 和Plugins 的区别

    IDEA 是一款常用的 Java 开发工具,它集成了 Maven 管理工具,可以方便地使用 Maven 来管理 Java 项目。在 IDEA 中,我们可以通过 Maven 的 Lifecycle 和 Plugins 来对项目进行构建和管理。这里我们来详细讲解这两者的区别。 Maven Lifecycle Maven 的 Lifecycle(生命周期)是指 M…

    Java 2023年5月20日
    00
  • springmvc mybatis集成配置示例

    下面是关于“SpringMVC MyBatis集成配置示例”的完整攻略,包含两个示例说明。 SpringMVC MyBatis集成配置示例 SpringMVC和MyBatis是Java Web应用程序开发中常用的框架。在本文中,将介绍如何使用这两个框架来构建一个Web应用程序。 步骤1:添加依赖 首先,我们需要在pom.xml文件中添加SpringMVC和M…

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