网站首页 > java教程 正文
为什么需要分布式锁
为了保证一个方法或数据在高并发情况下只能被一个线程执行或访问,在单机部署的情况下,可以使用Java并发相关的API或语法实现互斥,比如如ReentrantLock或Synchronized等。
随着业务发展的需要,单机部署的系统会演化成分布式集群部署,应用程序的线程分布在不同机器上,单机并发控制的锁策略也就失效了。为了解决这个问题,就需要跨虚拟机的分布式锁策略,来实现分布式系统的互斥和访问控制。
分布式锁应该具备以下特性:
- 互斥性:在分布式环境下,一个方法在同一时间只能被一个机器的一个线程执行;
- 高可用的获取锁与释放锁;
- 高性能的获取锁与释放锁;
- 具备可重入特性;
- 具备锁失效机制,防止死锁;
- 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。
分布式锁的实现方式
实现分布式锁的基本思路是:在集群的一个公共节点中,记录当前集群中哪个线程获得了资源锁,这个地方各个服务器的线程都可以访问得到。
如果有线程要访问资源,必须先查看是否有其他线程已经获取了资源的锁,如果没有那就可以占用资源锁,并记录在公共节点上。
分布式锁可以通过数据库、缓存或者ZooKeeper中间件来实现。
基于数据库实现分布式锁
基于数据库实现分布式锁,就是在数据库中针对需要进行互斥的方法建立一张锁表。
CREATE TABLE `method_lock` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`method_name` varchar(64) NOT NULL COMMENT '锁定的方法名',
`state` tinyint NOT NULL COMMENT '1:未分配;2:已分配',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`version` int NOT NULL COMMENT '版本号',
`PRIMARY KEY (`id`),
UNIQUE KEY `uidx_method_name` (`method_name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='锁定中的方法';
当有线程要调用某个方法时,在数据库的锁表中设置该方法的记录的状态为“已分配”。当有线程准备调用该方法时,看到锁表中已经设置了该方法的状态时,表示该方法已经被其他线程使用。当线程使用完方法,需要将该记录的状态字段设置为“未分配”,实现解锁。
基于缓存(Redis)实现分布式锁
基于数据库的方案由于每次执行方法都需要先对数据库进行IO操作,性能效率低下,基于redis的分布式锁更有实用价值。
当线程要调用需要互斥的方法时,在redis中先检查一下方法锁是否已经设置对应的key了,如果没有设置就表示可以获取锁。执行完方法需要将redis中的key进行重置。
对于单点部署,这个方案没有什么问题,在主从架构中,这个方案存在明显缺陷:
比如线程A从master节点获取到锁,在master节点将锁同步到slave节点前,master节点宕机了。随后slave节点升级为master节点,另外一个线程B此时仍然可以获取到锁,互斥失效!
基于ZooKeeper实现分布式锁
基于ZooKeeper的分布式锁方案可以解决上述问题,ZooKeeper集群的同步机制是可以支持最终一致性的。ZooKeeper实现分布式锁的方案如下:
(1)在Zookeeper当中先创建一个持久节点ParentLock。客户端Client_1尝试获取锁,需要在ParentLock下面创建一个临时顺序节点 Lock1。
(2)Client_1发现自己创建的临时顺序节点Lock1是顺序最靠前的,因此可以获得锁。
(3)Client_2也尝试获取锁,在ParentLock下再创建一个临时顺序节点Lock2。
(4)Client_2发现自己创建的节点Lock2前面还有Lock1,所以还不能获得锁。于是就建立对Lock1节点的监听。
(5)Client_3到来尝试获取锁,建立新的临时顺序节点Lock3。
(6)Client_3发现前面还有Lock2,不能获得锁,于是Client_3开始监听Lock2。
以上(1)-(6)是加锁的过程,锁的竞争者们通过临时顺序节点的创建和监听,形成了一个等待队列。
(7)Client1任务完成,删除节点Lock1。如果Client1因为某种原因断开连接,那么ZooKeeper也会删除对应的节点Lock1。
(8)Client2发现此时Lock2已经排在最前面了,Client2可以获得锁。
(9)Client2执行完成,删除节点Lock2,释放锁。这下轮到Client3获得锁了。
以上(7)-(9)是释放锁的过程。
总结
从性能角度来说,基于redis的方案性能最好;从可靠性角度来说,基于ZooKeeper的方案可靠性最高。合适的才是最好的,我们还是要根据自身需求来选择方案。
我会持续更新关于物联网、云原生以及数字科技方面的文章,用简单的语言描述复杂的技术,也会偶尔发表一下对IT产业的看法,欢迎大家关注,谢谢。
- 上一篇: 一文搞懂Redis分布式锁
- 下一篇: 一篇文章带你解读redis分布式锁的发展史和正确实现方式
猜你喜欢
- 2024-11-22 redis分布式锁
- 2024-11-22 4K字深度剖析redisson分布式锁原理
- 2024-11-22 java都为我们提供了各种锁,为什么还需要分布式锁?
- 2024-11-22 一文带你了解Java手写分布式锁的实现
- 2024-11-22 如何用Redisson框架实现分布式锁?
- 2024-11-22 基于 Redis 实现的分布式锁
- 2024-11-22 京东秒杀系统模块的Redis分布式锁深度剖析,没给你讲明白你打我
- 2024-11-22 面试官:Redis分布式锁超时了,任务还没执行完怎么办?
- 2024-11-22 聊聊Redis分布式锁
- 2024-11-22 浅谈分布式锁
你 发表评论:
欢迎- 最近发表
-
- 五,网络安全IDA Pro反汇编工具初识及逆向工程解密实战
- 「JAVA8」- Lambda 表达式(java lambda表达式原理)
- 深入探讨Java代码保护:虚拟机保护技术的新时代
- Nginx反向代理原理详解(图文全面总结)
- 逆向拆解日本IT,哪些Java技术栈薪资溢价高
- mybatis 逆向工程使用姿势不对,把表清空了,心里慌的一比
- Spring Boot集成ProGuard轻松实现Java 代码混淆, Java 应用固若金汤
- 从 Java 代码逆向工程生成 UML 类图和序列图
- 人与人相处:尊重是标配,靠谱是高配,厚道是顶配
- Windows系统安装日期如何修改(windows10怎么修改安装日期)
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)