摘要:如果應(yīng)用服務(wù)集群部署,則涉及到對(duì)分布式應(yīng)用加鎖。當(dāng)前分布式加鎖主要有三種方式磁盤數(shù)據(jù)庫緩存數(shù)據(jù)庫。以上代碼實(shí)現(xiàn)僅展示使用服務(wù)進(jìn)行加鎖訪問的便捷性,具體技術(shù)實(shí)現(xiàn)需要考慮死鎖鎖的檢查等情況,歡迎點(diǎn)擊分布式緩存服務(wù)了解更多。
場(chǎng)景介紹
很多互聯(lián)網(wǎng)場(chǎng)景(如商品秒殺,論壇回帖蓋樓等),需要用加鎖的方式,以對(duì)某種資源進(jìn)行順序訪問控制。如果應(yīng)用服務(wù)集群部署,則涉及到對(duì)分布式應(yīng)用加鎖。當(dāng)前分布式加鎖主要有三種方式:(磁盤)數(shù)據(jù)庫、緩存數(shù)據(jù)庫、Zookeeper。接下里讓我們一起看看加鎖實(shí)踐過程。
加鎖實(shí)現(xiàn)
package dcsDemo01;
import java.util.UUID;
import redis.clients.jedis.Jedis;
public class DistributedLock {
private final String host = "192.168.0.220"; private final int port = 6379; private static final String SUCCESS = "OK"; private static final String SET_IF_NOT_EXIST = "NX"; private static final String EXPIRE_TIME = "PX"; public DistributedLock(){} /* * @param lockName 鎖名 * @param timeout 獲取鎖的超時(shí)時(shí)間 * @param lockTimeout 鎖的有效時(shí)間 * @return 鎖的標(biāo)識(shí) */ public String getLockWithTimeout(String lockName, long timeout, long lockTimeout) { String ret = null; Jedis jedisClient = new Jedis(host, port); try { String authMsg = jedisClient.auth("Demo@123"); if (!SUCCESS.equals(authMsg)) { System.out.println("AUTH FAILED: " + authMsg); } String identifier = UUID.randomUUID().toString(); String lockKey = "DLock:" + lockName; long end = System.currentTimeMillis() + timeout; while(System.currentTimeMillis() < end) { String result = jedisClient.set(lockKey, identifier, SET_IF_NOT_EXIST, EXPIRE_TIME, lockTimeout); if(SUCCESS.equals(result)) { ret = identifier; break; } try { Thread.sleep(2); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } catch (Exception e) { e.printStackTrace(); }finally { jedisClient.quit(); jedisClient.close(); } return ret; } /* * @param lockName 鎖名 * @param identifier 鎖的標(biāo)識(shí) */ public void releaseLock(String lockName, String identifier) { Jedis jedisClient = new Jedis(host, port); try { String authMsg = jedisClient.auth("Demo@123"); if (!SUCCESS.equals(authMsg)) { System.out.println("AUTH FAILED: " + authMsg); } String lockKey = "DLock:" + lockName; if(identifier.equals(jedisClient.get(lockKey))) { jedisClient.del(lockKey); } } catch (Exception e) { e.printStackTrace(); }finally { jedisClient.quit(); jedisClient.close(); } }
}
測(cè)試代碼
假設(shè)20個(gè)線程對(duì)10臺(tái)mate10手機(jī)進(jìn)行搶購:
package dcsDemo01;
import java.util.UUID;
public class CaseTest {
public static void main(String[] args) { ServiceOrder service = new ServiceOrder(); for (int i = 0; i < 20; i++) { ThreadBuy client = new ThreadBuy(service); client.start(); } }
}
class ServiceOrder {
private final int MAX = 10; DistributedLock DLock = new DistributedLock(); int n = 10; public void handleOder() { String userName = UUID.randomUUID().toString().substring(0,8) + Thread.currentThread().getName(); String identifier = DLock.getLockWithTimeout("Huawei Mate 10", 10000, 2000); System.out.println("正在為用戶:" + userName + " 處理訂單"); if(n > 0) { int num = MAX - n + 1; System.out.println("用戶:"+ userName + "購買第" + num + "臺(tái),剩余" + (--n) + "臺(tái)"); }else { System.out.println("用戶:"+ userName + "無法購買,已售罄!"); } DLock.releaseLock("Huawei Mate 10", identifier); }
}
class ThreadBuy extends Thread {
private ServiceOrder service; public ThreadBuy(ServiceOrder service) { this.service = service; } @Override public void run() { service.handleOder(); }
}
運(yùn)行結(jié)果
配置好實(shí)際的緩存實(shí)例連接地址、端口與連接密碼,運(yùn)行代碼,得到以下結(jié)果:
正在為用戶:eee56fb7Thread-16 處理訂單
用戶:eee56fb7Thread-16購買第1臺(tái),剩余9臺(tái)
正在為用戶:d6521816Thread-2 處理訂單
用戶:d6521816Thread-2購買第2臺(tái),剩余8臺(tái)
正在為用戶:d7b3b983Thread-19 處理訂單
用戶:d7b3b983Thread-19購買第3臺(tái),剩余7臺(tái)
正在為用戶:36a6b97aThread-15 處理訂單
用戶:36a6b97aThread-15購買第4臺(tái),剩余6臺(tái)
正在為用戶:9a973456Thread-1 處理訂單
用戶:9a973456Thread-1購買第5臺(tái),剩余5臺(tái)
正在為用戶:03f1de9aThread-14 處理訂單
用戶:03f1de9aThread-14購買第6臺(tái),剩余4臺(tái)
正在為用戶:2c315ee6Thread-11 處理訂單
用戶:2c315ee6Thread-11購買第7臺(tái),剩余3臺(tái)
正在為用戶:2b03b7c0Thread-12 處理訂單
用戶:2b03b7c0Thread-12購買第8臺(tái),剩余2臺(tái)
正在為用戶:75f25749Thread-0 處理訂單
用戶:75f25749Thread-0購買第9臺(tái),剩余1臺(tái)
正在為用戶:26c71db5Thread-18 處理訂單
用戶:26c71db5Thread-18購買第10臺(tái),剩余0臺(tái)
正在為用戶:c32654dbThread-17 處理訂單
用戶:c32654dbThread-17無法購買,已售罄!
正在為用戶:df94370aThread-7 處理訂單
用戶:df94370aThread-7無法購買,已售罄!
正在為用戶:0af94cddThread-5 處理訂單
用戶:0af94cddThread-5無法購買,已售罄!
正在為用戶:e52428a4Thread-13 處理訂單
用戶:e52428a4Thread-13無法購買,已售罄!
正在為用戶:46f91208Thread-10 處理訂單
用戶:46f91208Thread-10無法購買,已售罄!
正在為用戶:e0ca87bbThread-9 處理訂單
用戶:e0ca87bbThread-9無法購買,已售罄!
正在為用戶:f385af9aThread-8 處理訂單
用戶:f385af9aThread-8無法購買,已售罄!
正在為用戶:46c5f498Thread-6 處理訂單
用戶:46c5f498Thread-6無法購買,已售罄!
正在為用戶:935e0f50Thread-3 處理訂單
用戶:935e0f50Thread-3無法購買,已售罄!
正在為用戶:d3eaae29Thread-4 處理訂單
用戶:d3eaae29Thread-4無法購買,已售罄!
不加鎖場(chǎng)景
如果注釋掉加鎖代碼,變成無鎖情況,則搶購無序。
//測(cè)試類中注釋兩行用于加鎖的代碼:
public void handleOder() {
String userName = UUID.randomUUID().toString().substring(0,8) + Thread.currentThread().getName(); //加鎖代碼 //String identifier = DLock.getLockWithTimeout("Huawei Mate 10", 10000, 2000); System.out.println("正在為用戶:" + userName + " 處理訂單"); if(n > 0) { int num = MAX - n + 1; System.out.println("用戶:"+ userName + "夠買第" + num + "臺(tái),剩余" + (--n) + "臺(tái)"); }else { System.out.println("用戶:"+ userName + "無法夠買,已售罄!"); } //加鎖代碼 //DLock.releaseLock("Huawei Mate 10", identifier);
}
注釋加鎖代碼后的運(yùn)行結(jié)果,可以看出處理過程是無序的:
正在為用戶:e04934ddThread-5 處理訂單
正在為用戶:a4554180Thread-0 處理訂單
用戶:a4554180Thread-0購買第2臺(tái),剩余8臺(tái)
正在為用戶:b58eb811Thread-10 處理訂單
用戶:b58eb811Thread-10購買第3臺(tái),剩余7臺(tái)
正在為用戶:e8391c0eThread-19 處理訂單
正在為用戶:21fd133aThread-13 處理訂單
正在為用戶:1dd04ff4Thread-6 處理訂單
用戶:1dd04ff4Thread-6購買第6臺(tái),剩余4臺(tái)
正在為用戶:e5977112Thread-3 處理訂單
正在為用戶:4d7a8a2bThread-4 處理訂單
用戶:e5977112Thread-3購買第7臺(tái),剩余3臺(tái)
正在為用戶:18967410Thread-15 處理訂單
用戶:18967410Thread-15購買第9臺(tái),剩余1臺(tái)
正在為用戶:e4f51568Thread-14 處理訂單
用戶:21fd133aThread-13購買第5臺(tái),剩余5臺(tái)
用戶:e8391c0eThread-19購買第4臺(tái),剩余6臺(tái)
正在為用戶:d895d3f1Thread-12 處理訂單
用戶:d895d3f1Thread-12無法購買,已售罄!
正在為用戶:7b8d2526Thread-11 處理訂單
用戶:7b8d2526Thread-11無法購買,已售罄!
正在為用戶:d7ca1779Thread-8 處理訂單
用戶:d7ca1779Thread-8無法購買,已售罄!
正在為用戶:74fca0ecThread-1 處理訂單
用戶:74fca0ecThread-1無法購買,已售罄!
用戶:e04934ddThread-5購買第1臺(tái),剩余9臺(tái)
用戶:e4f51568Thread-14購買第10臺(tái),剩余0臺(tái)
正在為用戶:aae76a83Thread-7 處理訂單
用戶:aae76a83Thread-7無法購買,已售罄!
正在為用戶:c638d2cfThread-2 處理訂單
用戶:c638d2cfThread-2無法購買,已售罄!
正在為用戶:2de29a4eThread-17 處理訂單
用戶:2de29a4eThread-17無法購買,已售罄!
正在為用戶:40a46ba0Thread-18 處理訂單
用戶:40a46ba0Thread-18無法購買,已售罄!
正在為用戶:211fd9c7Thread-9 處理訂單
用戶:211fd9c7Thread-9無法購買,已售罄!
正在為用戶:911b83fcThread-16 處理訂單
用戶:911b83fcThread-16無法購買,已售罄!
用戶:4d7a8a2bThread-4購買第8臺(tái),剩余2臺(tái)
總的來說,使用DCS服務(wù)中Redis類型的緩存實(shí)例實(shí)現(xiàn)分布式加鎖,有幾大優(yōu)勢(shì):
1、加鎖操作簡(jiǎn)單,使用SET、GET、DEL等幾條簡(jiǎn)單命令即可實(shí)現(xiàn)鎖的獲取和釋放。
2、性能優(yōu)越,緩存數(shù)據(jù)的讀寫優(yōu)于磁盤數(shù)據(jù)庫與Zookeeper。
3、可靠性強(qiáng),DCS有主備和集群實(shí)例類型,避免單點(diǎn)故障。
以上代碼實(shí)現(xiàn)僅展示使用DCS服務(wù)進(jìn)行加鎖訪問的便捷性,具體技術(shù)實(shí)現(xiàn)需要考慮死鎖、鎖的檢查等情況,歡迎點(diǎn)擊分布式緩存服務(wù)DCS了解更多。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/62000.html
摘要:今天整理了一下近大半年以來的一些文章,和我的預(yù)期一樣,很多文章我都忘記自己曾經(jīng)寫過了,這個(gè)記錄的過程讓我也有了新的理解。希望大家,收藏,點(diǎn)贊,加轉(zhuǎn)發(fā)。 今天整理了一下近大半年以來的一些文章,和我的預(yù)期一樣,很多文章我都忘記自己曾經(jīng)寫過了,這個(gè)記錄的過程讓我也有了新的理解。希望大家,收藏,點(diǎn)贊,加轉(zhuǎn)發(fā)。 面試必備 面試必備:深入Spring MVC DispatchServlet 源碼...
摘要:今天整理了一下近大半年以來的一些文章,和我的預(yù)期一樣,很多文章我都忘記自己曾經(jīng)寫過了,這個(gè)記錄的過程讓我也有了新的理解。希望大家,收藏,點(diǎn)贊,加轉(zhuǎn)發(fā)。 今天整理了一下近大半年以來的一些文章,和我的預(yù)期一樣,很多文章我都忘記自己曾經(jīng)寫過了,這個(gè)記錄的過程讓我也有了新的理解。希望大家,收藏,點(diǎn)贊,加轉(zhuǎn)發(fā)。 面試必備 面試必備:深入Spring MVC DispatchServlet 源碼...
摘要:多線程編程這篇文章分析了多線程的優(yōu)缺點(diǎn),如何創(chuàng)建多線程,分享了線程安全和線程通信線程池等等一些知識(shí)。 中間件技術(shù)入門教程 中間件技術(shù)入門教程,本博客介紹了 ESB、MQ、JMS 的一些知識(shí)... SpringBoot 多數(shù)據(jù)源 SpringBoot 使用主從數(shù)據(jù)源 簡(jiǎn)易的后臺(tái)管理權(quán)限設(shè)計(jì) 從零開始搭建自己權(quán)限管理框架 Docker 多步構(gòu)建更小的 Java 鏡像 Docker Jav...
摘要:大家好,我是冰河有句話叫做投資啥都不如投資自己的回報(bào)率高。馬上就十一國慶假期了,給小伙伴們分享下,從小白程序員到大廠高級(jí)技術(shù)專家我看過哪些技術(shù)類書籍。 大家好,我是...
閱讀 2715·2021-10-14 09:47
閱讀 5014·2021-09-22 15:52
閱讀 3404·2019-08-30 15:53
閱讀 1496·2019-08-30 15:44
閱讀 738·2019-08-29 16:41
閱讀 1723·2019-08-29 16:28
閱讀 492·2019-08-29 15:23
閱讀 1687·2019-08-26 12:20