Java 分布式锁:原理与实践

在分布式系统中,多个节点同时操作共享资源的情况非常普遍。为了保证数据的一致性,分布式锁 应运而生。分布式锁 是一种跨多个服务器的互斥锁,用于协调分布式环境下的资源访问。

本文将介绍 Java 实现分布式锁 的几种常见方式,并结合 Redis、Zookeeper 等框架给出代码实例,探讨如何在实际项目中运用这些技术。


在这里插入图片描述

一、什么是分布式锁?

分布式锁的本质是保证在分布式环境下,多个进程或者节点可以按照正确的顺序来访问共享资源,避免数据冲突。具体场景包括:

  1. 限流控制:确保多个服务器上的相同任务不会被重复执行。
  2. 资源竞争:多个节点对数据库或文件等共享资源的并发修改。
  3. 任务调度:某些任务在同一时间只能由一个节点执行。

二、分布式锁的常见实现方式

常见的分布式锁实现方式有:

  1. 基于数据库的分布式锁
  2. 基于 Redis 的分布式锁
  3. 基于 Zookeeper 的分布式锁

下面我们分别介绍这些方法及其在 Java 中的实现。


三、基于数据库的分布式锁

1. 使用数据库表实现分布式锁

通过创建一张 锁表,在锁定资源时插入一条记录,通过 唯一约束 来确保同一时间只有一个节点能成功插入数据。

代码示例:
CREATE TABLE distributed_lock (lock_name VARCHAR(64) PRIMARY KEY,locked_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Java 实现:

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;@Service
public class DatabaseLockService {private final JdbcTemplate jdbcTemplate;public DatabaseLockService(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}public boolean acquireLock(String lockName) {try {jdbcTemplate.update("INSERT INTO distributed_lock (lock_name) VALUES (?)", lockName);return true;} catch (Exception e) {return false; // 如果插入失败,表示锁已经被占用}}public void releaseLock(String lockName) {jdbcTemplate.update("DELETE FROM distributed_lock WHERE lock_name = ?", lockName);}
}

注意事项

  • 加锁失败的处理:如果锁已被占用,可能需要设置重试机制或返回失败。
  • 锁超时机制:为了避免死锁,可以在数据库中设置锁的超时时间。

四、基于 Redis 的分布式锁

Redis 是分布式锁最常用的实现之一,主要依赖于 Redis 提供的 SETNX(SET if Not Exists)命令,来确保只有一个客户端能成功设置某个键值。

1. 使用 Redis 实现分布式锁

代码示例:
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class RedisLockService {private final StringRedisTemplate redisTemplate;public RedisLockService(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}public boolean acquireLock(String lockKey, String lockValue, long timeout) {Boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, timeout, TimeUnit.SECONDS);return success != null && success;}public void releaseLock(String lockKey, String lockValue) {String currentValue = redisTemplate.opsForValue().get(lockKey);if (lockValue.equals(currentValue)) {redisTemplate.delete(lockKey); // 删除锁}}
}

注意事项

  • 过期时间:设置锁的过期时间,避免因某个节点宕机导致死锁。
  • 锁的唯一标识:每个锁需要有一个唯一标识,释放锁时必须检查该标识是否一致,防止误删他人的锁。

2. 基于 Redisson 实现分布式锁

Redisson 是 Redis 官方推荐的 Java 客户端,提供了原生的分布式锁实现。

代码示例:
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class RedissonLockService {private final RedissonClient redissonClient;public RedissonLockService(RedissonClient redissonClient) {this.redissonClient = redissonClient;}public void lock(String lockKey) {RLock lock = redissonClient.getLock(lockKey);lock.lock(10, TimeUnit.SECONDS); // 获取锁并设置过期时间}public void unlock(String lockKey) {RLock lock = redissonClient.getLock(lockKey);if (lock.isHeldByCurrentThread()) {lock.unlock();}}
}

优点

  • 原生支持锁的自动续期:避免因超时导致的锁失效。
  • 高性能:Redisson 提供了分布式锁的高效实现,适合高并发场景。

五、基于 Zookeeper 的分布式锁

Zookeeper 是一个分布式协调服务,天生支持分布式锁的实现。Zookeeper 中的 临时节点顺序节点 是实现分布式锁的关键。

1. 使用 Zookeeper 实现分布式锁

通过 Zookeeper 的 临时顺序节点 实现锁机制,保证只有第一个创建的节点可以获取锁,其他节点需要监听前一个节点的删除事件。

代码示例:
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;import java.io.IOException;
import java.util.Collections;
import java.util.List;public class ZookeeperLockService {private final ZooKeeper zooKeeper;private String lockPath;public ZookeeperLockService(String zkAddress) throws IOException {zooKeeper = new ZooKeeper(zkAddress, 3000, null);}public boolean acquireLock(String lockName) throws Exception {lockPath = zooKeeper.create("/locks/" + lockName + "/lock_", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);List<String> children = zooKeeper.getChildren("/locks/" + lockName, false);Collections.sort(children);String smallestChild = children.get(0);if (lockPath.endsWith(smallestChild)) {return true; // 获取锁成功} else {// 等待前一个节点删除int previousNodeIndex = Collections.binarySearch(children, lockPath.substring(lockPath.lastIndexOf('/') + 1)) - 1;String previousNodePath = "/locks/" + lockName + "/" + children.get(previousNodeIndex);Stat stat = zooKeeper.exists(previousNodePath, new LockWatcher());return stat == null;}}public void releaseLock() throws Exception {zooKeeper.delete(lockPath, -1);}private class LockWatcher implements Watcher {@Overridepublic void process(WatchedEvent event) {synchronized (this) {this.notifyAll(); // 通知等待的线程}}}
}

注意事项

  • 节点监听:Zookeeper 提供了节点监听机制,确保当锁被释放时,等待中的节点能够及时获取锁。
  • 临时节点:使用临时节点确保当某个节点挂掉时,锁能够自动释放。

六、分布式锁的最佳实践

  1. 设置过期时间:无论使用 Redis 还是 Zookeeper,都应该为锁设置过期时间,防止死锁。
  2. 锁的粒度:锁的粒度应当尽量细,避免将过多的操作放在同一个锁中,影响系统并发性能。
  3. 重试机制:当锁获取失败时,可以采用 重试机制,或根据业务场景采取合适的降级方案。

七、总结

分布式锁在分布式系统中是一个非常重要的工具,尤其是在处理资源竞争、任务调度等场景时。RedisZookeeper 是目前最常用的分布式锁实现方式。

  • Redis 的分布式锁实现简单高效,适合大多数高并发场景,结合 Redisson 可以进一步简化开发。
  • Zookeeper 的分布式锁适用于需要严格一致性的场景,得益于其 临时顺序节点,能够提供更可靠的锁机制。

在实际使用中,开发者需要根据业务需求选择合适的分布式锁方案,并合理配置锁的过期时间和重试机制,避免因锁竞争或死锁导致的系统性能下降。

分布式锁是保障分布式系统一致性的基础,而合理的锁机制设计能极大提升系统的稳定性和并发处理能力。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

OpenAI API: How to catch all 5xx errors in Python?

题意&#xff1a;OpenAI API&#xff1a;如何在 Python 中捕获所有 5xx 错误&#xff1f; 问题背景&#xff1a; I want to catch all 5xx errors (e.g., 500) that OpenAI API sends so that I can retry before giving up and reporting an exception. 我想捕获 OpenAI API…

浙大数据结构:05-树8 File Transfer

数据结构MOOC PTA习题 这道题考察并查集的操作&#xff0c;合并以及找根结点 机翻&#xff1a; 1、条件准备 node是数组存放1-N结点的根节点的&#xff0c;n为总结点数 #include <iostream> using namespace std;const int N 1e4 5; int node[N]; int n; 先初始化…

<<编码>> 第 16 章 存储器组织(3)--3-8 译码器 示例电路

3-8 译码器 info::操作说明 “写入” 开关先断开(Q 为低电平, 表示不写入) S2-S1-S0 设置一个二进制数, 选中 Q0~Q7 其中一个作为 Q 的输出 “数据输入” 端置入要保存的数(0或1) 闭合 “写入” 开关, 对应的一位锁存器中的 W 为高电平, 表示可以写入, “数据输入” 的值最终…

嵌入式常用GUI介绍

目录 前言一、GuiLite二、LVGL三、SimpleGUI四、MiniGUI五、emWin六、TouchGFX七、uGUI八、GFX九、Embedded Wizard十、CrankSoftware十一、PEG Graphics Software十二、Guiliani十三、MPLAB Harmony Graphics Suite 前言 图形用户界面&#xff08;Graphical User Interface&am…

关系数据库设计之Armstrong公理详解

~犬&#x1f4f0;余~ “我欲贱而贵&#xff0c;愚而智&#xff0c;贫而富&#xff0c;可乎&#xff1f; 曰&#xff1a;其唯学乎” 一、Armstrong公理简介 Armstrong公理是一组在关系数据库理论中用于推导属性依赖的基本规则。这些公理是以著名计算机科学家威廉阿姆斯特朗&…

优化内存工具 | RAM Saver Pro v24.9 便携版

RAM Saver是一款专业的RAM优化工具&#xff0c;旨在提高计算机的性能和运行速度。它通过多种优化技术&#xff0c;如内存碎片整理、CPU和主板缓存效率提升、恢复内存等&#xff0c;为应用程序提供更多的内存资源&#xff0c;从而使系统运行更加流畅。适合所有需要优化内存使用和…

EMT-LTR--学习任务间关系的多目标多任务优化

EMT-LTR–学习任务间关系的多目标多任务优化 title&#xff1a; Learning Task Relationships in Evolutionary Multitasking for Multiobjective Continuous Optimization author&#xff1a; Zefeng Chen, Yuren Zhou, Xiaoyu He, and Jun Zhang. journal&#xff1a; IEE…

银河麒麟V10系统崩溃后的处理

银河麒麟V10系统崩溃后的处理 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 当银河麒麟桌面操作系统V10崩溃无法启动时&#xff0c;直接使用备份还原工具不可行。此时&#xff0c;应采取以下步骤&#xff1a; 进入救援模式或LiveCD&#x…

攻防世界---->Windows_Reverse1

学习笔记。 前言&#xff1a;不会&#xff0c;代码越简洁&#xff0c;越难受 T ^ T 下载 查壳。 UPX脱壳。 此题脱壳后的程序&#xff0c;是不能运行的。 网上wp&#xff0c;说是因为作者采用了ASLR(地址随机化) 解决方法&#xff1a;一&#xff1a;用XP运行调试。 方法二&a…

0基础跟德姆(dom)一起学AI 数据处理和统计分析05-Pandas数分入门

* DataFrame读写文件 * DataFrame加载部分数据 * DataFrame分组聚合计算 * DataFrame常用排序方式 * DataFrame案例-链家数据分析 --- 1.DataFrame-保存数据到文件 * 格式 python df对象.to_数据格式(路径) # 例如: df.to_csv(data/abc.csv) * 代码演示 > 如…

Deepin man 没有关于printf 的手册页条目

问题 man 3 printf&#xff1a;在第 3 节中没有关于 printf 的手册页条目。 解决方法&#xff1a;安装manpages发开库。 搜索包 apt search manpages安装 sudo apt install manpages-dev若没有manpages-dev&#xff0c;安装manpages-posix-dev&#xff0c;应该也可以&#x…

【成品论文】2024年华为杯研赛E题25页高质量成品论文(后续会更新

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片链接&#xff0c;那是获取资料的入口&#xff01; 点击链接加入【2024华为杯研赛资料汇总】&#xff1a;https://qm.qq.com/q/Mxv2XNWxUc https://qm.qq.com/q/Mxv2XNWxUc 高速公路应急车道紧急启用模型…

深度学习02-pytorch-03-张量的数值计算

张量&#xff08;Tensor&#xff09;是多维数组的通用化概念&#xff0c;它可以表示标量&#xff08;0维&#xff09;、向量&#xff08;1维&#xff09;、矩阵&#xff08;2维&#xff09;以及更高维度的数据。在深度学习和数值计算中&#xff0c;张量是基础数据结构&#xff…

基于python的api扫描器系统的设计与实现

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

MySQL练手题--周内每天销售情况(困难)

一、准备工作 Create table If Not Exists Orders (order_id int, customer_id int, order_date date, item_id varchar(30), quantity int); Create table If Not Exists Items (item_id varchar(30), item_name varchar(30), item_category varchar(30)); Truncate table Or…

【软件文档】软件项目试运行方案(word实际套用2024)

软件项目试运行方案&#xff08;Word原件参考&#xff09; 一、试运行目的 二、试运行的准备 三、试运行时间 四、试运行制度 五、试运行具体内容与要求 软件全套资料部分文档清单&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c…

python画图1

import matplotlib.pyplot as pltplt.rcParams["font.sans-serif"] ["SimHei"]# 模拟数据 years [2016, 2017, 2018, 2019, 2020, 2021, 2022] market_size [7950, 8931, 9940, 11205, 12305, 13199, 14980] my_color #3e9df5plt.plot(years, market_s…

《他们的奇妙时光》圆满收官,葛秋谷新型霸总获好评

9月21日&#xff0c;由王枫、张开法执导&#xff0c;周洁琼、葛秋谷领衔主演的奇幻爱情题材都市喜剧《他们的奇妙时光》圆满收官。该剧讲述了意外被游戏角色刑天附体的设计师宋灵灵&#xff0c;为修复游戏漏洞&#xff0c;被迫与能压制刑天的甲方总裁萧然同居&#xff0c;两人在…

局域网设备自动发现常用方法

文章目录 需求实现方法ARP (Address Resolution Protocol)Ping ip的流程抓包如下代码实现 mDNS 对比测试Avahi 介绍Avahi 安装Avahi 使用测试代码 需求 局域网设备自动发现是软件开发中的一个常见且重要的需求&#xff0c;它简化了设备间的协作机制&#xff0c;降低了软件各模…

MySQL内存(Buffer Pool)

Buffer Pool MySQL 的数据存在磁盘&#xff0c;但是不能每次读取数据都从磁盘里去&#xff0c;这样磁盘IO太频繁&#xff0c;存在性能问题。 InnoDB设计了一个缓存池&#xff08;Buffer Pool&#xff09;&#xff0c;缓冲池在内存中。 默认配置Buffer Pool大小为128MB&#xf…