1.并发场景下JDK锁的局限性
使用JDK的锁在单节点上能很好解决并发安全问题,可一旦到了分布式场景下,JDK的锁就会捅娄子了。
1.1. 初始代码
上图是个Controller本身这controller的OrderNumGenerator已经解决了并发安全问题
<code>package
cn.enjoy.lock;import
cn.enjoy.utils.FileId;import
java.text.SimpleDateFormat;import
java.util.Date;import
java.util.concurrent.locks.ReentrantLock;public
class
OrderNumGenerator
{private
java.util.concurrent.locks.Lock lock =new
ReentrantLock(); /<code>
1.2. 启动2个tomcat
修改springboot配置文件application.properties
修改端口为8081
使用启动类分别启动8080,8081
1.3. 配置nginx访问
1.4. 使用JMeter测试
把测试结果排序后
虽然单个节点解决了并发安全问题,但是在分布式场景下,依然出现了并发安全问题
2. 使用分布式锁解决
通过上面的案例使用JDK的锁解决不了分布式场景下的并发安全问题,接下来就考虑使用分布式锁来解决了。
2.1. 解决方案概述
分布式锁有很多种方案
这些方案可以使用一个设计模式来统一
2.2. 模板方法模式
2.2.1. 模板方法介绍
在父类中编排主流程,将步骤实现延迟到子类去实现。
上图网上购物的时候总体4个流程
- 清点商品
- 计算价格
- 支付(未知扩展)
- 送货上门
2.2.2. 代码实现
<code>package
cn.enjoy.template;import
java.util.ArrayList;import
java.util.HashMap;import
java.util.List;import
java.util.Map;public
abstract
class
AbstractTemplate
{public
void
shopping
()
{ Map cars =new
HashMap(); cars.put("电池"
,10f
); cars.put("娃娃"
,20f
); cars.put("打气筒"
,30f
); /<code>
对于支付来说是【未知的扩展】因此它是个抽象的方法,如果想使用微信支付可以重现pay方法
2.3. 分布式锁的模板方法
2.3.1. 概览
1、定义锁的接口Lock
2、在AbstractLock模板锁里面实现getLock方法,实现通用的逻辑。
3、不能确实的步骤,作为虚拟方法,甩锅给子类实现。
4、子类只需要聚焦自己的小步骤逻辑,实现tryLock,waitLock,unLock方法
2.3.2. 代码实现
实现tryLock,waitLock,与Lock接口的unLock方法
2.4. MySql实现方式
2.4.1. 实现思路
利用数据库自身提供的锁机制实现,要求数据库支持行级锁;
2.4.2. 实现流程
2.4.3. 代码
2.4.3.1. Mysql分布式锁的实现
2.4.3.2. 修改Controller
增加分布式锁
2.4.4. 测试
搞定分布式安全问题
2.5. ZK实现方式
2.5.1. 实现思路
基于zk的节点特性以及watch机制实现;
2.5.2. 实现流程
2.5.3. 代码
2.5.3.1. tryLock
尝试创建 /lock 节点,如果创建成功获得锁
2.5.3.2. unLock
操作完毕后释放锁
2.5.3.3. waitLock
使用countdownLatch 当/lock节点被删除再发送通知。