Java中如何实现分布式锁,详细教程来啦~

创作不易,如果觉得这篇文章对你有帮助,欢迎各位老铁点个赞支持下呗,您的支持是我创作的最大动力!

1 前言

随着时代的变化,Java应用层面也发生了巨大的变化。前几年还是使用单体应用,现在流行的是分布式系统。那么,分布式系统也带来了一系列问题,比如说:数据的一致性、服务的高可用性、多线程环境下,如何保证某个业务操作,只有一个线程可以执行。本文主要介绍下,分布式系统下,如何使用分布式锁,来保证数据的安全性。

2 什么是分布式架构

分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。随着互联网的飞速发展,用户量急剧增多,互联网产品越来越多样化,内容越来越多,传统的单体应用结构系统已经无法满足需求,分布式系统就应运而生。

分布式系统通过服务化,即SOA架构的方式,采用分而治之的策略,通过业务上合适的拆分,将海量用户的访问量进行拆分分流,以满足系统的高可用性高性能可伸缩可扩展的需求。

在介绍分布式锁内容之前,先来简单介绍下,什么是分布式,与传统的单应用服务架构有什么区别,以便于更好地理解分布式锁。

2.1 基础概念

  • 分布:
    分布指的是在一定的范围内散布开,分布的反义词集中

  • 集中式架构:
    就是把所有的程序、功能、模块都集中到一个项目中,部署在一台服务器上,从而对外提供服务(单体架构、单体服务、单体应用)。

    这种结构在前几年很流行,当前互联网流行的是分布式架构,因为现在的业务系统很复杂,集中式架构已经不能够满足业务需要,回想一下,前些年还是一个项目,打一个war包,扔到Tomcat部署,就满足了业务需要,现在,远远不能满足当下的业务需求,所以,分布式架构应运而生。

  • 分布式架构:
    就是把所有的程序、功能、模块拆分成不同的子项目,部署在多台不同的服务器上,这些子项目相互协作,共同对外提供服务。

    直白来说,就是有很多项目,有很多jar(war)包,这些项目相互协作才能完成需要的功能,满足业务的需要。

    简单来说,就是之前的一个单体应用(后台管理系统),通过拆分,拆分成用户中心、产品中心、客户中心等多个小应用服务,这种把一个大的单体应用项目,拆分成多个小应用项目的方式,就是分布式系统应用

2.2 集群部署和分布式部署的区别

  • 集群
    集群就是将相同的程序、功能,部署在两台或多台服务器上,这些服务器对外提供的功能是完全一样的,集群是通过不断横向扩展增加服务器的方式,以提高服务的能力。

  • 分布式
    分布式就是将两个或多个程序、功能分别运行在两台或多台主机服务器上,这些服务器对外提供的功能并不一样,它们通过相互协作最终完成某一个服务或功能。

简单地来说,如果两台或者多台服务器部署的程序功能完全一样就是集群(常见的有Redis集群,MySQL主从复制,Nginx高可用,Zookeeper集群,服务应用集群等等),程序功能如果不一样就是分布式

分布式中的每一个服务节点,都可以做集群(根据服务压力决定部署几台服务器集群),而集群并不一定就是分布式的。

2.3 分布式架构带来哪些问题

分布式系统设计的本质就是:如何合理地将一个系统拆分为多个子系统,然后部署到多台不同服务器上,对外体统高可用的服务。

2.3.1 分布式架构需要解决的问题

分布式架构需要解决如下几个问题:

  • 如何合理的拆分系统
  • 拆分后的各个子系统之间如何通信(RPC)
  • 如何适应不断增长的业务需求,使其具有良好的可扩展性
  • 如何保证子系统的可靠性和数据的最终一致性
  • 分布式事务的问题
  • 分布式锁的问题
  • 分布式系统的缓存
  • 分布式系统的搜索
  • 分布式系统的配置中心

所以相比较传统的单体应用,分布式架构带来了很大的复杂性,也带来了很多问题。好在也慢慢的出现了很多开源技术解决方案,进行分布式架构就需要使用很多技术,想要真正的掌握分布式系统还是有一定难度的。

一致性问题:
分布式系统中的一致性指应用系统的一致性和数据的一致性。多个系统之间互相通信,就有可能遇到例如:

  • 调用超时:系统A调用系统B超时,系统A得到超时反馈,但不确定系统B是否已经处理结束,就会造成不一致,此时可能需要使用分布式事务,来保证数据的一致性。
  • 缓存与数据库的不一致:面对海量的访问请求,我们经常会需要在数据库前加一层缓存,这个缓存与数据库之间如何保证数据的一致性。

2.3.2 CAP理论

CAP定理,指的是在一个分布式系统中,Consistency(一致性)Availability(可用性)Partition tolerance(分区容错性),最多实现两点,三者不可兼得。CAP定理主要是针对分布式系统各节点之间的通信提出的定理。

  • 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。

  • 可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的请求。

  • 分区容错性(P):分布式系统中的节点之间的通信有可能失败,在这种情况下,系统要能够容纳这种错误。

    分区容忍P,主要是指网络问题, 比如:A 、B、C 三台机器之间相互ping不通、网络不通,这种情况在分布式系统里面是允许的,也是很有可能发生的,我们要容忍这种情况的出现,在这种情况出现的时候,我们是选择 “一致性的C” 还是选择 “可用性的A”,就需要看应用场景。

分布式系统中必然存在着分区,而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容错性P是我们必须需要实现的。那么根据CAP定理,C与A我们只能择其一实现,分布式系统P是基础,P都满足不了就成单机了。在满足P的基础上,一致性C和可用性A无法同时满足,需要根据业务场景进行抉择。比如说:Eureka强调高可用性APZooKeeper强调的一致性CP

那么为什么实现P的情况下,C与A不可兼得呢,我们来分析一下。

假设节点A与节点B,它们中的数据是一致的,然后我们发起请求,修改了节点A中的数据,节点B中的数据也要相应的更改,但是出现了网络延迟等问题,节点B中的数据没有更改,依然是旧数据。这时,请求节点B中的数据,但是数据还没有同步,那怎么办呢?如果我们要满足一致性,就应阻塞这次请求,等节点B中的数据得到更改,这样可用性就无法满足。

如果我们要满足可用性,那么我们就要返回节点B中的旧的数据,这样一致性就无法满足。所以我们就需要结合实际情况,做出取舍,是满足一致性还是满足可用性。

限于篇幅,本文主要介绍下分布式锁的使用。

3 锁的分类

Java中,锁的种类有很多,大概有以下几种:
乐观锁/悲观锁
独享锁/共享锁
互斥锁/读写锁
可重入锁
公平锁/非公平锁
分段锁
偏向锁/轻量级锁/重量级锁
自旋锁

限于篇幅,这里不再啰嗦,需要了解的童鞋们,可以参考我另一篇博客:https://blog.csdn.net/smilehappiness/article/details/107123259

4 实现分布式锁

4.1 为什么要使用分布式锁

使用分布式锁的目的,为了保证一个方法在高并发情况下,同一时间只能被同一个线程执行,即保证同一时间只有一个客户端可以对共享资源进行操作

在传统单体应用单机部署的情况下,可以使用Java并发处理相关的API(如ReentrantLcok或synchronized)进行互斥控制。但是,随着业务发展的需要,原单体单机部署的系统被演化成分布式系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题。

4.2 使用分布式锁的场景

分布式部署,需要保证高并发情况下,同一时间只能被同一个线程执行,即保证同一时间只有一个客户端可以对共享资源进行操作。否则会导致数据一致性问题或者其他问题,比如:金额改成了负数,商品数改成了负数等等。

使用分布式锁,解决的一个典型的场景问题:商品超卖问题

4.2.1 商品超卖问题

单节点部署方案:
在这里插入图片描述
用户下单流程:

  • 用户发起下单请求
  • 查询库存是否充足
  • 库存充足则生成订单并扣减库存
    (此处存在分布式事务的问题,本文不予讨论)
  • 响应用户下单成功或失败

单节点部署在并发量很小的时候还是挺正常的,整个流程的响应速度也算乐观,但是订单系统或库存系统其中任意一台服务down掉,都会中断整个业务流程。(单节点部署,耦合度过高,存在单点故障)。

需要注意的是,这里的库存系统需要通过synchronized来控制并发操作的,否则此方案依然会出现线程安全问题。

因此才决定要改用分布式集群部署方案解决单点故障,提高系统可用性

分布式集群部署方案:
在这里插入图片描述
改用分布式集群部署方案后的流程图如上所示,看起来只是将订单系统与库存系统多部署了几个节点而已,但是这样在整个业务流程上却有所不同,首先我们先来分析一下此方案为什么会出现线程安全问题。

场景如下:
张三、李四同时向订单系统进行下单,由于订单系统的负载均衡策略,将张三的请求交给了订单系统1去处理,李四的请求交给了订单系统2去处理。
订单系统1查询了库存系统1,发现库存充足,订单系统2查询了库存系统2,发现库存充足。
订单系统1、订单系统2都发现库存充足,于是分别通知库存系统1、库存系统2进行扣减库存,这就导致了库存系统中商品数量被扣减为负数的情况,也叫做库存超卖现象

出现这个问题的根本原因是分布式集群节点之间无法共享synchronized锁。既然问题已经分析出来了,那么解决问题方案自然就呼之欲出了。我们可以使用一把能够跨应用共享的分布式锁,锁住扣减库存的过程,这样一来在订单系统扣减库存的过程中,就不允许有其他的订单系统执行相同的操作。这样就可以保证了分布式集群环境下的线程安全性问题。

超卖这部分内容笔者懒得写了,直接摘自一篇不错的博客:https://blog.csdn.net/qq_39914581/article/details/88817171

4.3 实现分布式锁的几种方式

4.3.1 基于Zookeeper实现分布式锁

限于篇幅,该部分内容另外写了一篇博客,有需要的童鞋们,可以参考:https://blog.csdn.net/smilehappiness/article/details/107751865

4.3.2 基于Redis实现分布式锁(推荐)

限于篇幅,该部分内容另外写了一篇博客,有需要的童鞋们,可以参考:如何基于Redis实现分布式锁,详细教程拿走不送

4.3.3 基于数据库实现分布式锁

后续更新…

参考资料链接:
https://www.cnblogs.com/hustzzl/p/9343797.html

https://www.jianshu.com/p/1b139f037b15
https://www.jianshu.com/p/350a5f891f11
https://blog.csdn.net/wuzhiwei549/article/details/80692278

写博客是为了记住自己容易忘记的东西,另外也是对自己工作的总结,希望尽自己的努力,做到更好,大家一起努力进步!

如果有什么问题,欢迎大家评论,一起探讨,代码如有问题,欢迎各位大神指正!

给自己的梦想添加一双翅膀,让它可以在天空中自由自在的飞翔!

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值