Java分布式锁实战指南:从理论到实践

前言

在分布式系统中,传统的单机锁机制无法满足跨进程、跨机器的同步需求。分布式锁应运而生,成为保证分布式系统数据一致性的关键技术。本文将全面介绍Java中分布式锁的实现方式和最佳实践。

1. 分布式锁的核心概念

1.1 为什么需要分布式锁?

在分布式环境中,多个服务实例可能同时访问共享资源,需要一种跨JVM的同步机制:

// 传统单机锁在分布式环境中失效
public class OrderService {private final Object lock = new Object(); // 只在当前JVM有效public void createOrder() {synchronized(lock) {// 在分布式环境中,其他节点的线程仍然可以同时执行}}
}

1.2 分布式锁的基本要求

  • 互斥性:同一时刻只有一个客户端能持有锁
  • 可重入性:同一个客户端可以多次获取同一把锁
  • 超时机制:避免死锁,自动释放过期锁
  • 高可用:锁服务需要高可用性
  • 高性能:获取和释放锁的操作要高效

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

2.1 基于唯一索引的实现

// 数据库表结构
CREATE TABLE distributed_lock (id BIGINT PRIMARY KEY AUTO_INCREMENT,lock_key VARCHAR(64) NOT NULL UNIQUE,lock_value VARCHAR(255) NOT NULL,expire_time DATETIME NOT NULL,create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
// 基于MySQL的分布式锁实现
public class MySQLDistributedLock {private final DataSource dataSource;private final String lockKey;private final String lockValue;public boolean tryLock(long expireMillis) {try (Connection conn = dataSource.getConnection()) {String sql = "INSERT INTO distributed_lock (lock_key, lock_value, expire_time) " +"VALUES (?, ?, DATE_ADD(NOW(), INTERVAL ? MILLISECOND)) " +"ON DUPLICATE KEY UPDATE " +"lock_value = IF(expire_time < NOW(), VALUES(lock_value), lock_value), " +"expire_time = IF(expire_time < NOW(), VALUES(expire_time), expire_time)";PreparedStatement ps = conn.prepareStatement(sql);ps.setString(1, lockKey);ps.setString(2, lockValue);ps.setLong(3, expireMillis);return ps.executeUpdate() > 0;} catch (SQLException e) {return false;}}public void unlock() {try (Connection conn = dataSource.getConnection()) {String sql = "DELETE FROM distributed_lock WHERE lock_key = ? AND lock_value = ?";PreparedStatement ps = conn.prepareStatement(sql);ps.setString(1, lockKey);ps.setString(2, lockValue);ps.executeUpdate();} catch (SQLException e) {// 日志记录}}
}

2.2 优缺点分析

优点

  • 实现简单,依赖少
  • 理解容易,适合小型项目

缺点

  • 性能瓶颈,数据库压力大
  • 非阻塞操作实现复杂
  • 需要处理数据库连接问题

3. 基于Redis的分布式锁

3.1 使用Redisson客户端

<!-- Maven依赖 -->
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.17.0</version>
</dependency>
// Redisson分布式锁示例
public class RedisDistributedLockExample {private final RedissonClient redisson;public void performTask() {RLock lock = redisson.getLock("myDistributedLock");try {// 尝试获取锁,最多等待10秒,锁过期时间30秒boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);if (isLocked) {// 执行业务逻辑executeBusinessLogic();}} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}}private void executeBusinessLogic() {// 业务代码}
}

3.2 手动实现Redis分布式锁

public class ManualRedisLock {private final JedisPool jedisPool;private static final String LOCK_SCRIPT = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then " +"return redis.call('pexpire', KEYS[1], ARGV[2]) " +"else return 0 end";private static final String UNLOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"return redis.call('del', KEYS[1]) " +"else return 0 end";public boolean tryLock(String lockKey, String lockValue, long expireMillis) {try (Jedis jedis = jedisPool.getResource()) {Object result = jedis.eval(LOCK_SCRIPT, Collections.singletonList(lockKey),Arrays.asList(lockValue, String.valueOf(expireMillis)));return "1".equals(result.toString());}}public boolean unlock(String lockKey, String lockValue) {try (Jedis jedis = jedisPool.getResource()) {Object result = jedis.eval(UNLOCK_SCRIPT,Collections.singletonList(lockKey),Collections.singletonList(lockValue));return "1".equals(result.toString());}}
}

4. 基于ZooKeeper的分布式锁

4.1 Curator框架实现

<dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>5.3.0</version>
</dependency>
public class ZookeeperDistributedLock {private final CuratorFramework client;private final String lockPath;public void executeWithLock() {InterProcessMutex lock = new InterProcessMutex(client, lockPath);try {if (lock.acquire(10, TimeUnit.SECONDS)) {try {// 获得锁后的业务逻辑processBusiness();} finally {lock.release();}}} catch (Exception e) {// 处理异常}}private void processBusiness() {// 业务处理}
}

4.2 ZooKeeper锁原理

ZooKeeper通过临时顺序节点实现分布式锁:

  1. 客户端在锁目录下创建临时顺序节点
  2. 检查自己是否是最小序号的节点
  3. 如果是,获得锁;如果不是,监听前一个节点
  4. 完成操作后删除节点

5. Spring Boot整合分布式锁

5.1 基于Spring的分布式锁抽象

@Component
public class DistributedLockManager {@Autowiredprivate RedissonClient redissonClient;public <T> T executeWithLock(String lockKey, long waitTime, long leaseTime, Supplier<T> supplier) {RLock lock = redissonClient.getLock(lockKey);try {if (lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS)) {return supplier.get();}throw new RuntimeException("获取锁失败");} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException("锁获取被中断", e);} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}}
}

5.2 注解方式使用分布式锁

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributedLock {String key(); // 锁的keylong waitTime() default 5000; // 等待时间long leaseTime() default 30000; // 持有时间
}
@Aspect
@Component
public class DistributedLockAspect {@Autowiredprivate DistributedLockManager lockManager;@Around("@annotation(distributedLock)")public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {String lockKey = distributedLock.key();return lockManager.executeWithLock(lockKey, distributedLock.waitTime(),distributedLock.leaseTime(),() -> {try {return joinPoint.proceed();} catch (Throwable throwable) {throw new RuntimeException(throwable);}});}
}

6. 分布式锁的最佳实践

6.1 锁key的设计原则

public class LockKeyGenerator {public static String generateLockKey(String prefix, String businessKey) {return String.format("lock:%s:%s", prefix, businessKey);}public static String generateOrderLockKey(Long orderId) {return generateLockKey("order", String.valueOf(orderId));}
}

6.2 异常处理和重试机制

public class LockRetryTemplate {private final int maxRetries;private final long retryInterval;public <T> T executeWithRetry(Callable<T> task, String lockKey) {int retries = 0;while (retries < maxRetries) {try {return task.call();} catch (LockAcquisitionException e) {retries++;if (retries >= maxRetries) {throw new RuntimeException("获取锁重试次数超限", e);}try {Thread.sleep(retryInterval);} catch (InterruptedException ie) {Thread.currentThread().interrupt();throw new RuntimeException("重试被中断", ie);}} catch (Exception e) {throw new RuntimeException("业务执行异常", e);}}throw new RuntimeException("未知异常");}
}

6.3 监控和告警

@Component
public class LockMonitor {private final MeterRegistry meterRegistry;@EventListenerpublic void onLockEvent(LockEvent event) {meterRegistry.counter("distributed.lock.operation", "type", event.getType().name(),"success", String.valueOf(event.isSuccess())).increment();if (!event.isSuccess()) {// 发送告警sendAlert(event);}}private void sendAlert(LockEvent event) {// 实现告警逻辑}
}

7. 不同场景下的选择建议

7.1 技术选型对比

方案性能可靠性实现复杂度适用场景
数据库锁低频操作,数据一致性要求高
Redis锁高频操作,允许偶尔失败
ZooKeeper锁强一致性要求,复杂锁场景

7.2 推荐方案

  1. 一般业务场景:Redis + Redisson
  2. 金融级一致性:ZooKeeper + Curator
  3. 简单低频场景:数据库实现
  4. 云原生环境:使用云服务商提供的分布式锁服务

8. 常见问题及解决方案

8.1 锁过期时间设置

// 动态调整锁超时时间
public class AdaptiveLockTimeout {private long baseTimeout = 30000; // 基础超时30秒private long maxTimeout = 120000; // 最大超时2分钟public long calculateTimeout(String businessType) {// 根据业务类型和历史执行时间动态计算超时long estimatedTime = estimateExecutionTime(businessType);return Math.min(baseTimeout + estimatedTime * 2, maxTimeout);}
}

8.2 避免死锁

// 锁超时自动释放
public class SafeDistributedLock {public boolean tryLockWithTimeout(String lockKey, long timeout) {long start = System.currentTimeMillis();while (System.currentTimeMillis() - start < timeout) {if (tryAcquireLock(lockKey)) {return true;}try {Thread.sleep(100); // 短暂等待} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}return false;}
}

总结

分布式锁是分布式系统中的重要组件,选择合适的技术方案需要综合考虑性能、可靠性、复杂度等因素。建议:

  1. 优先使用成熟框架如Redisson或Curator
  2. 合理设计锁粒度,避免过度使用分布式锁
  3. 实现完善的监控,及时发现和处理锁问题
  4. 考虑最终一致性方案,减少对分布式锁的依赖

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

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

相关文章

(二叉树) 本节目标 1. 掌握树的基本概念 2. 掌握二叉树概念及特性 3. 掌握二叉树的基本操作 4. 完成二叉树相关的面试题练习

二叉树1. 树型结构&#xff08;了解&#xff09;1.1 概念1.2 概念&#xff08;重要&#xff09;1.3 树的表示形式&#xff08;了解&#xff09;1.4 树的应用2. 二叉树&#xff08;重点&#xff09;2.1 概念2.2 两种特殊的二叉树2.3 二叉树的性质2.4 二叉树的存储2.5 二叉树的基…

【Zephyr电源与功耗专题】13_PMU电源驱动介绍

文章目录前言一、PMU系统介绍二、Zephyr系统下驱动PMU的组成2.1&#xff1a;PMU系统在Zephyr上包括五大部分&#xff1a;2.2&#xff1a;功能说明2.3&#xff1a;B-core功能说明(Freertos)三、PMU各驱动API详解3.1:Power_domain3.1.1&#xff1a;初始化3.1.2&#xff1a;rpmsg回…

华清远见25072班网络编程学习day5

作业0> 将IO多路复用实现TCP并发服务器实现一遍程序源码&#xff1a;#include <25072head.h> #define SER_IP "192.168.153.128" //服务器ip地址 #define SER_PORT 8888 //服务器端口号 int main(int argc, const char *argv[]) {//1、创建一个…

【数据结构--顺序表】

顺序表和链表 1.线性表&#xff1a; 线性表是n个具有相同特性&#xff08;相同逻辑结构&#xff0c;物理结构&#xff09;的数据元素的有限序列。常见的线性表有&#xff1a;顺序表&#xff0c;链表&#xff0c;栈&#xff0c;队列&#xff0c;字符串…线性表在逻辑上是线性结构…

【PyTorch】图像多分类部署

如果需要在独立于训练脚本的新脚本中部署模型&#xff0c;这种情况模型和权重在内存中不存在&#xff0c;因此需要构造一个模型类的对象&#xff0c;然后将存储的权重加载到模型中。加载模型参数&#xff0c;验证模型的性能&#xff0c;并在测试数据集上部署模型from torch imp…

FS950R08A6P2B 双通道汽车级IGBT模块Infineon英飞凌 电子元器件核心解析

一、核心解析&#xff1a;FS950R08A6P2B 是什么&#xff1f;1. 电子元器件类型FS950R08A6P2B 是英飞凌&#xff08;Infineon&#xff09; 推出的一款 950A/800V 双通道汽车级IGBT模块&#xff0c;属于功率半导体模块。它采用 EasyPACK 2B 封装&#xff0c;集成多个IGBT芯片和二…

【系列文章】Linux中的并发与竞争[05]-互斥量

【系列文章】Linux中的并发与竞争[05]-互斥量 该文章为系列文章&#xff1a;Linux中的并发与竞争中的第5篇 该系列的导航页连接&#xff1a; 【系列文章】Linux中的并发与竞争-导航页 文章目录【系列文章】Linux中的并发与竞争[05]-互斥量一、互斥锁二、实验程序的编写2.1驱动…

TensorRT 10.13.3: Limitations

Limitations Shuffle-op can not be transformed to no-op for perf improvement in some cases. For the NCHW32 format, TensorRT takes the third-to-last dimension as the channel dimension. When a Shuffle-op is added like [N, ‘C’, H, 1] -> [‘N’, C, H], the…

Python与Go结合

Python与Go结合的方法Python和Go可以通过多种方式结合使用&#xff0c;通常采用跨语言通信或集成的方式。以下是几种常见的方法&#xff1a;使用CFFI或CGO进行绑定Python可以通过CFFI&#xff08;C Foreign Function Interface&#xff09;调用Go编写的库&#xff0c;而Go可以通…

C++ 在 Visual Studio Release 模式下,调试运行与直接运行 EXE 的区别

前言 在 Visual Studio (以下简称 VS) 中开发 C 项目时&#xff0c;我们常常需要在 Debug 和 Release 两种构建模式之间切换。Debug 模式适合开发和调试&#xff0c;而 Release 模式则针对生产环境&#xff0c;进行代码优化以提升性能。然而&#xff0c;即使在 Release 模式下&…

南京方言数据集|300小时高质量自然对话音频|专业录音棚采集|方言语音识别模型训练|情感计算研究|方言保护文化遗产数字化|语音情感识别|方言对话系统开发

引言与背景 随着人工智能技术的快速发展&#xff0c;语音识别和自然语言处理领域对高质量方言数据的需求日益增长。南京方言作为江淮官话的重要分支&#xff0c;承载着丰富的地域文化和语言特色&#xff0c;在语言学研究和方言保护方面具有重要价值。本数据集精心采集了300小时…

基于LSTM深度学习的电动汽车电池荷电状态(SOC)预测

基于LSTM深度学习的电动汽车电池荷电状态&#xff08;SOC&#xff09;预测 摘要 电动汽车&#xff08;EV&#xff09;的普及对电池管理系统&#xff08;BMS&#xff09;提出了极高的要求。电池荷电状态&#xff08;State of Charge, SOC&#xff09;作为BMS最核心的参数之一&am…

Golang语言之数组、切片与子切片

一、数组先记住数组的核心特点&#xff1a;盒子大小一旦定了就改不了&#xff08;长度固定&#xff09;&#xff0c;但盒子里的东西能换&#xff08;元素值可变&#xff09;。就像你买了个能装 3 个苹果的铁皮盒&#xff0c;想多装 1 个都不行&#xff0c;但里面的苹果可以换成…

速通ACM省铜第四天 赋源码(G-C-D, Unlucky!)

目录 引言&#xff1a; G-C-D, Unlucky! 题意分析 逻辑梳理 代码实现 结语&#xff1a; 引言&#xff1a; 因为今天打了个ICPC网络赛&#xff0c;导致坐牢了一下午&#xff0c;没什么时间打题目了&#xff0c;就打了一道题&#xff0c;所以&#xff0c;今天我们就只讲一题了&…

数据链路层总结

目录 &#xff08;一&#xff09;以太网&#xff08;IEEE 802.3&#xff09; &#xff08;1&#xff09;以太网的帧格式 &#xff08;2&#xff09;帧协议类型字段 ①ARP协议 &#xff08;横跨网络层和数据链路层的协议&#xff09; ②RARP协议 &#xff08;二&#xff…

Scala 新手实战三案例:从循环到条件,搞定基础编程场景

Scala 新手实战三案例&#xff1a;从循环到条件&#xff0c;搞定基础编程场景 对 Scala 新手来说&#xff0c;单纯记语法容易 “学完就忘”&#xff0c;而通过小而精的实战案例巩固知识点&#xff0c;是掌握语言的关键。本文精选三个高频基础场景 ——9 乘 9 乘法口诀表、成绩等…

java学习笔记----标识符与变量

1.什么是标识符?Java中变量、方法、类等要素命名时使用的字符序列&#xff0c;称为标识符。 技巧:凡是自己可以起名字的地方都叫标识符。 比如:类名、方法名、变量名、包名、常量名等 2.标识符的命名规则由26个英文字母大小写&#xff0c;0-9&#xff0c;或$组成 数字不可以开…

AI产品经理面试宝典第93天:Embedding技术选型与场景化应用指南

1. Embedding技术演进全景解析 1.1 稀疏向量:关键词匹配的基石 1.1.1 问:请说明稀疏向量的适用场景及技术特点 答:稀疏向量适用于关键词精确匹配场景,典型实现包括TF-IDF、BM25和SPLADE。其技术特征表现为50,000+高维向量且95%以上位置为零值,通过余弦或点积计算相似度…

【Mermaid.js】从入门到精通:完美处理节点中的空格、括号和特殊字符

文章标签&#xff1a; Mermaid, Markdown, 前端开发, 数据可视化, 流程图 文章摘要&#xff1a; 你是否在使用 Mermaid.js 绘制流程图时&#xff0c;仅仅因为节点文本里加了一个空格或括号&#xff0c;整个图就渲染失败了&#xff1f;别担心&#xff0c;这几乎是每个 Mermaid 新…

多技术融合提升环境生态水文、土地土壤、农业大气等领域的数据分析与项目科研水平

一&#xff1a;空间数据获取与制图1.1 软件安装与应用1.2 空间数据介绍1.3海量空间数据下载1.4 ArcGIS软件快速入门1.5 Geodatabase地理数据库二&#xff1a;ArcGIS专题地图制作2.1专题地图制作规范2.2 空间数据的准备与处理2.3 空间数据可视化&#xff1a;地图符号与注记2.4 研…