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節點被刪除再發送通知。