Java中从常见的分布式锁解决方案

在Java分布式项目中,分布式锁用于确保在分布式系统环境下,对共享资源的访问能够同步进行,防止数据不一致的问题。常见的分布式锁实现方式主要有基于数据库、基于缓存(如Redis)、基于ZooKeeper等。

1. 基于数据库的分布式锁

基于数据库实现分布式锁主要是通过数据库的唯一索引或行锁特性来保证锁的唯一性。

  • 使用唯一索引:在数据库中创建一个锁表,表中有一个唯一索引字段。当某个服务实例需要获取锁时,它尝试在该表中插入一条具有唯一索引值的记录。如果插入成功,表示获取锁成功;如果因为唯一索引冲突而失败,则表示锁已被其他实例获取。
  • 使用行锁:选定一个表的某一行作为锁标识,通过对这一行进行更新操作来尝试获取锁(比如更新一个时间戳字段),利用数据库的行锁机制来实现分布式锁。

2. 基于缓存的分布式锁(以Redis为例)

Redis分布式锁的核心思想是利用Redis的原子命令来创建一个锁。最常用的方法是通过SET命令加上某些选项,如NX(只在键不存在时设置键)、PX(键的过期时间,以毫秒为单位)来实现。

基于RedLock算法的实现

在Java中,我们可以使用Redisson客户端库来实现RedLock算法,Redisson已经提供了对RedLock的封装,使得在Java中实现RedLock变得相对简单。

首先,确保你的项目中加入了Redisson依赖。如果是使用Maven,可以在pom.xml中添加以下依赖:

<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>最新版本</version>
</dependency>

接下来,是一个使用Redisson实现RedLock的简单示例:

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;import java.util.concurrent.TimeUnit;public class RedLockExample {public static void main(String[] args) {// 配置每个Redis实例Config config1 = new Config();config1.useSingleServer().setAddress("redis://127.0.0.1:6379");RedissonClient redissonClient1 = Redisson.create(config1);Config config2 = new Config();config2.useSingleServer().setAddress("redis://127.0.0.2:6379");RedissonClient redissonClient2 = Redisson.create(config2);Config config3 = new Config();config3.useSingleServer().setAddress("redis://127.0.0.3:6379");RedissonClient redissonClient3 = Redisson.create(config3);// 获取三个Redis实例上的锁对象RLock lock1 = redissonClient1.getLock("myLock");RLock lock2 = redissonClient2.getLock("myLock");RLock lock3 = redissonClient3.getLock("myLock");// 创建RedLock(红锁)RLock redLock = redissonClient1.getRedLock(lock1, lock2, lock3);try {// 尝试获取锁,最多等待100秒,锁定后最多持有锁60秒boolean isLocked = redLock.tryLock(100, 60, TimeUnit.SECONDS);if (isLocked) {// 如果获取到锁,执行业务逻辑try {System.out.println("Lock acquired, executing some task");} finally {// 无论如何,最后都要释放锁redLock.unlock();System.out.println("Lock released");}}} catch (InterruptedException e) {e.printStackTrace();} finally {// 关闭Redisson客户端redissonClient1.shutdown();redissonClient2.shutdown();redissonClient3.shutdown();}}
}

这个例子中,我们首先创建了三个指向不同Redis实例的Redisson客户端。然后,我们从每个客户端获取了一个锁对象,并使用这些锁对象创建了一个RedLock。接着,我们尝试获取这个RedLock,如果成功,就执行一些业务逻辑,最后释放锁并关闭客户端。
在Redisson的RedLock实现中,当你尝试获取锁时,底层机制涉及到了键(key)和值(value)的设置,但这些都被Redisson内部封装了起来,因此在使用Redisson时你不需要直接操作这些底层细节。

获取锁的工作原理:

当调用redLock.tryLock()方法时,Redisson会对每个参与的Redis实例执行以下操作:

  1. 生成一个唯一的锁标识(UUID):这个UUID是作为value存储在Redis中的,每次尝试获取锁时都会生成一个新的UUID。这保证了锁的公平性和唯一性。

  2. 尝试在所有配置的Redis实例上设置锁:Redisson会向每个Redis实例发送一个带有NX(只在键不存在时设置键)和PX(键的过期时间,单位是毫秒)选项的SET命令,其中键是你指定的锁名(如"myLock"),值是第一步中生成的UUID。这个操作是基于Redis的SET命令实现的,确保了操作的原子性。

  3. 计算成功获取锁的实例数量:如果成功在大多数Redis实例上设置了键,则认为成功获取了锁。具体来说,假设参与的Redis实例总数为N,那么至少需要在N/2+1个实例上成功设置了键,锁才算获取成功。

  4. 检查锁获取时间:为了确保锁的有效性,Redisson还会检查获取锁所花费的时间。如果获取锁的时间超过了从第一个Redis实例开始尝试到最后一个Redis实例尝试的总时间,则认为获取锁失败,此时会自动释放在前面步骤中成功设置的所有Redis实例上的锁。

释放锁:

当调用redLock.unlock()方法时,Redisson会在每个Redis实例上执行一段Lua脚本。这段脚本会检查给定键的值是否与尝试获取锁时生成的UUID相匹配。如果匹配,则删除该键,释放锁。这个过程确保了只有锁的持有者能够释放锁。

封装细节:

由于Redisson已经封装了这些操作,因此使用Redisson时,你不需要直接设置key和value。你只需要通过getLock方法指定锁名,并通过tryLockunlock方法来获取和释放锁。这使得使用Redisson实现分布式锁变得非常简单和直接,同时也隐藏了实现的复杂性。

3. 基于ZooKeeper的分布式锁

ZooKeeper实现分布式锁的思路是利用其节点(ZNode)的唯一性和临时性(临时节点在客户端断开连接时自动删除)特性。

举例说明

  • 创建一个代表锁的持久节点,比如/mylock
  • 当一个客户端尝试获得锁时,它在/mylock下创建一个临时顺序节点,如/mylock/lock_00000001
  • 客户端获取/mylock下所有子节点,并比较自己创建的节点序号。如果该节点序号最小,那么认为该客户端获得了锁。
  • 当持有锁的客户端完成其任务后,它会删除自己创建的那个临时顺序节点。同时,剩下的等待锁的客户端会收到ZooKeeper的通知,重新执行第3步的逻辑,以此类推。
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;public class ZooKeeperLock {private ZooKeeper zooKeeper;public ZooKeeperLock(ZooKeeper zooKeeper) {this.zooKeeper = zooKeeper;}// 方法实现...
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/795735.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL面试题系列-10

MySQL是一个关系型数据库管理系统&#xff0c;由瑞典 MySQL AB 公司开发&#xff0c;属于 Oracle 旗下产品。MySQL是最流行的关系型数据库管理系统之一&#xff0c;在 WEB 应用方面&#xff0c;MySQL是最好的RDBMS (Relational Database Management System&#xff0c;关系数据…

数据结构之堆底层实现的循序渐进

题外话 把没写的都补回来! 正题 堆 概念 堆是一棵完全二叉树&#xff0c;因此可以层序的规则采用顺序的方式来高效存储&#xff0c; 大根堆:指根结点比左右孩子都大的堆 小根堆:指根结点比左右孩子都小的堆 性质 1.堆中某个节点的值总是不大于或不小于其父节点的值 2…

鸿蒙OS元服务开发:【(Stage模型)设置应用主窗口】

一、设置应用主窗口说明 在Stage模型下&#xff0c;应用主窗口由UIAbility创建并维护生命周期。在UIAbility的onWindowStageCreate回调中&#xff0c;通过WindowStage获取应用主窗口&#xff0c;即可对其进行属性设置等操作。还可以在应用配置文件中设置应用主窗口的属性&…

每日一题(leetcode1026):节点与其祖先的最大差值--dfs

考虑到只能计算祖先之间的节点差而不能计算兄弟之间的节点差&#xff0c;所以思考使用dfs来解决该题。 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), ri…

嵌入式开发学习---(部分)数据结构(无代码)

数据结构 为什么学习数据结构&#xff1f; 1&#xff09;c语言告诉如何写程序&#xff0c;数据结构是如何简洁高效的写程序 2&#xff09;遇到一个实际问题&#xff0c;需要写程序去实现相应功能&#xff0c;需要解决那两个方面的问题&#xff1f; 如何表达数据之间的逻辑规律…

背包问题---

一、背包模型 有一个体积为V的背包,商店有n个物品,每个物品有一个价值v和体积w,每个物品只能被拿一次,问能够装下物品的最大价值。 这里每一种物品只有两种状态即"拿"或"不拿". 设状态dp[i][j]表示到第i个物品为止,拿的物品总体积为j的情况下的最大价…

一、持续集成介绍

持续集成介绍 一、什么是持续集成二、持续集成的流程三、持续集成的组成要素四、持续集成的好处 一、什么是持续集成 持续集成&#xff08;CI&#xff09;指的是&#xff0c;频繁地&#xff08;一天多次&#xff09;将代码集成到主干。持续集成的目的&#xff0c;就是让产品可…

LeetCode:1483. 树节点的第 K 个祖先(倍增 Java)

目录 1483. 树节点的第 K 个祖先 题目描述&#xff1a; 实现代码与解析&#xff1a; 倍增 原理思路&#xff1a; 1483. 树节点的第 K 个祖先 题目描述&#xff1a; 给你一棵树&#xff0c;树上有 n 个节点&#xff0c;按从 0 到 n-1 编号。树以父节点数组的形式给出&#…

软件测试——黑盒测试

黑盒测试也就是针对功能进行测试&#xff0c;白盒测试就是后端根据自己的代码逻辑进行一下自测&#xff0c;灰盒测试就是黑盒和白盒的混合测试。 1.黑盒测试主要发现以下类型的错误 对比需求规格说明书功能遗漏或者不一致。 接口错误 数据库连接访问错误 效率不高&#xff…

【好书推荐-第十四期】《 互联网大厂晋升指南:从P5到P9的升级攻略》

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公众号&#xff1a;洲与AI。 &#x1f388; 本文专栏&#xff1a;本文收录…

第八讲 Sort Aggregate 算法

我们现在将讨论如何使用迄今为止讨论过的 DBMS 组件来执行查询。 1 查询计划【Query Plan】 我们首先来看当一个查询【Query】被解析【Parsed】后会发生什么&#xff1f; 当 SQL 查询被提供给数据库执行引擎&#xff0c;它将通过语法解析器进行检查&#xff0c;然后它会被转换…

机器人力觉控制(力源)原理及力矩传感器性能分析

机器人力控原理及其性能分析 在机器人的操作任务中&#xff0c;处理机器人和环境之间的物理接触是非常重要的。由于机器人系统的复杂性和不确定性&#xff0c;纯运动控制往往是不够的&#xff0c;因为即使是最精确的模型也无法完全准确地预测所有可能的情况。 当机器人在与环境…

arm开发板移植工具mkfs.ext4

文章目录 一、前言二、手动安装e2fsprogs1、下载源码包2、解压源码3、配置4、编译5、安装 三、移植四、验证五、总结 一、前言 在buildroot菜单中&#xff0c;可以通过勾选e2fsprogs工具来安装mkfs.ext4工具&#xff1a; Target packages -> Filesystem and flash utilit…

vue3中mars3d通过滑动条去改变地图图层的透明度

效果图 加滑动条 因为我这个存在单选框&#xff0c;在点击滑动条的时候 会出现将单选框选中的问题&#xff0c;所以用了一个div把滑动条包裹起来并加了冒泡 changeLiveSituationBg方法 // 改变底图显示颜色 val是我点击这个单选框对应值 const changeLiveSituationBg va…

5G智慧地铁数字孪生可视化平台,推进铁路行业数字化转型

随着科技的快速发展&#xff0c;5G智慧地铁数字孪生可视化平台正逐渐成为铁路行业数字化转型的重要推动力。巨蟹数科数字孪生平台集成了5G通信技术、大数据分析、云计算和人工智能等先进技术&#xff0c;通过构建数字孪生模型&#xff0c;实现对地铁运营全过程的实时监控、预测…

互联网大厂都在用的DevOps工具,看看你会几样?

关注公众号&#xff1a;“DevOps实战派”&#xff0c;获取更多DevOps和运维的精彩内容。 DevOps 是一种强调开发与 IT 运营之间合作的软件开发范式&#xff0c;主要依靠自动化来优化流程、提高生产力并确保及时、可靠的软件交付。 下面&#xff0c;我将介绍目前在互联网大厂中…

嵌入式实用网站

参考资料篇 ✔ 正点原子官网&#xff1a; 正点原子资料下载中心 — 正点原子资料下载中心 1.0.0 文档 ✔ LVGL百问网 &#x1f3a8;百问网LVGL中文教程手册文档 — 百问网LVGL中文教程手册文档 1.0 文档 ✔ Freertos开发文档 FreeRTOS - Quick start guide ✔ Linux命令大全 Li…

【7】双向循环链表

【7】双向循环链表 1、双向循环链表2、添加3、删除 1、双向循环链表 &#x1f58a; 头节点的 prev 指向尾节点 &#x1f58a; 尾节点的 next 指向头节点 2、添加 /*** 往索引位置添加元素*/Overridepublic void add(int index, E element) {checkIndex4Add(index);if (index s…

《QT实用小工具·十三》FlatUI辅助类之各种炫酷的控件集合

1、概述 源码放在文章末尾 FlatUI辅助类之各种炫酷的控件集合 按钮样式设置。文本框样式设置。进度条样式。滑块条样式。单选框样式。滚动条样式。可自由设置对象的高度宽度大小等。自带默认参数值。 下面是demo演示&#xff1a; 项目部分代码如下所示&#xff1a; #ifnd…

抖音运营技巧2

短视频起号的7步骤 1、自我分析 先明确自己的兴趣爱好&#xff0c;有什么特长及能力 你能给粉丝带来什么价值&#xff0c;目标用户是谁 2、找对标账号 通过自我分析做出账号定位选择参与的赛道 找最少10个优秀同行(三个阶段级别的账号) 3、搭建账号主页 头像、昵称、主…