【多線程】單例模式和阻塞隊列

目錄

一.單例模式

1. 餓漢模式

2. 懶漢模式

?二.阻塞隊列

1. 阻塞隊列的概念

2. BlockingQueue接口

3.生產者-消費者模型

4.模擬生產者-消費者模型


一.單例模式

單例模式(Singleton Pattern)是一種常用的軟件設計模式,其核心思想是確保一個類只有一個實例,并提供一個全局訪問點來獲取這個實例。

為什么要引入單例模式?

單例模式的核心就是一個類中只有一個實例,因為只用管理一個實例,那么就可以更好的對代碼進行一個校驗和檢查,方便高效管理,同時也避免了多個實例可能帶來的問題。

如果創建一個實例需要耗費100G的資源,那么創建出多個實例,代價太大。而且在多數情況下,一個實例完全夠用,所有沒有必要創建出多個實例,這樣就避免資源的重復創建和浪費


在編譯器中,沒有提供類只能創建出多少個實例的方法,但是我們可以通過一些代碼邏輯去規定創建實例的要求,下面是常用的幾種單例模式。

1. 餓漢模式

核心特點是?在類加載時就立即創建單例實例,并通過靜態方法提供全局訪問。

class Singleton{private static Singleton singleton = new Singleton();public static Singleton getSingleton(){return singleton;}//核心操作private Singleton(){}}
  1. 使用static關鍵字保證唯一實例(在類被加載的時候就會創建出這個唯一實例)
  2. 構造方法被設為私有,導致構造方法無法被調用
  3. 如果想要獲取這個實例只能使用靜態方法getSingleton調用

?由于在類被加載的時候,就會創建出實例,創建實例的時機很早(感覺非常的迫切,像一個餓漢),所有叫做餓漢模式

?注意:在多線程中,并發調用getSingleton靜態方法,由于只有讀操作,所以是線程安全

缺點: 如果實例未被使用,實例依然會被創建,可能造成資源浪費(假設實例的大小是100G)。


2. 懶漢模式

其核心特點是?延遲實例的創建,只有在第一次使用時才初始化單例對象,以減少資源浪費。

public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}// 非線程安全public static LazySingleton getInstance() {if (instance == null) {//這里會涉及指令重排序的問題instance = new LazySingleton();}return instance;}
}

在使用調用方法時,只有在第一次使用時才初始化實例,否則都是返回已經存在的實例


注意:?在多線程中,并發調用getInstance方法,由于同時存在兩個線程修改一個變量操作,線程不安全

?發現出現new兩次情況,所以解決這個問題,我們要進行加鎖

class LazySingleton {private static LazySingleton instance;private LazySingleton() {}static Object A = new Object();// 非線程安全public static LazySingleton getInstance() {synchronized (A){if (instance == null) {//這里會涉及指令重排序的問題instance = new LazySingleton();}}return instance;}
}

?這里我們會發現,每次調用getInstance方法,都要進行加鎖和解鎖的步驟,這樣的步驟開銷很大

所以我們需要進行改進

    public static LazySingleton getInstance() {if(instance==null){synchronized (A){if (instance == null) {//這里會涉及指令重排序的問題instance = new LazySingleton();}}}return instance;}
  • 第一次的? if? 語句判斷是否需要加鎖
  • 第二次的? if? ?語句判斷是否為空

?寫到這里我們的代碼還存在一個很嚴重的問題,由于指令重排序引起的線程安全問題

                    instance = new LazySingleton();

?在創建一個實例的時候,主要有3步驟:正確的步驟順序是1—>2—>3

1. 在內存中開辟一份空間2.使用構造方法去創建實例,3. 將空間的地址賦值給引用變量

但是JVM可能會將步驟的執行順序發送改變1—>3—>2,從而引發線程安全問題


具體原因:t2線程沒有進入因為鎖阻塞這步,t2線程會在t1線程執行完地址賦值后,剛好執行第一次的 if 判斷語句,發現引用變量不為空,會直接返回引用變量(但是引用變量的值是空的)?

其實解決也很簡單使用volatile關鍵字

class LazySingleton {private static  volatile LazySingleton instance;private LazySingleton() {}static Object A = new Object();// 非線程安全public static LazySingleton getInstance() {if(instance==null){synchronized (A){if (instance == null) {//這里會涉及指令重排序的問題instance = new LazySingleton();}}}return instance;}
}

?二.阻塞隊列

1. 阻塞隊列的概念

阻塞隊列可以看成是普通隊列的一種擴展,遵循先進先出的原則,核心特性是當隊列操作無法立即執行時,線程會被自動阻塞直到條件滿足。

  • 如果隊列是滿的,進行入隊列操作則會被阻塞,直到隊列不滿
  • 如果隊列是空的,進行出隊列操作則會被阻塞,直到隊列不空

2. BlockingQueue接口

在 Java?標準庫中內置了阻塞隊列. 如果我們需要在一些程序中使用阻塞隊列, 直接使用標準庫中的

  • BlockingQueue接口屬于java.util.concurrent包。它是線程安全的隊列,支持阻塞操作。
  • 常見的實現類有:ArrayBlockingQueue,LinkedBlockingQueue,PriorityBlockingQueue
方法說明

put(E e)

隊列未滿時插入元素;若隊列已滿,則阻塞線程直到有空位。

take()

隊列非空時取出元素;若隊列為空,則阻塞線程直到有元素可用。

offer(E e)

隊列未滿時插入元素并返回?true;隊列已滿時直接返回?false(非阻塞)。

poll()

隊列非空時取出元素并返回;隊列為空時返回?null(非阻塞)。

offer(E e, long timeout, TimeUnit unit)

隊列滿時等待指定超時時間,超時后返回?false

poll(long timeout, TimeUnit unit)

隊列空時等待指定超時時間,超時后返回?null

E peek()返回隊列頭部元素但不移除;隊列空時返回?null

其中只有put( )和take( )方法帶有阻塞的效果

3.生產者-消費者模型

生產者-消費者模型用于解決多線程環境下的線程同步問題,核心思想是通過?共享緩沖區(阻塞隊列)?解耦生產者和消費者,使兩者可以獨立并發工作

生產者和消費者彼此之間不直接進行聯系,而是通過緩沖區(阻塞隊列)進行聯系?

好處

(1)解耦合

  1. 解耦生產者和消費者的直接依賴,生產者和消費者可獨立開發,提高開發效率,方便維護
  2. ?生產者和消費者可并行執行,最大化利用 CPU、I/O 等資源
  3. 即使生產者掛了,也不會影響消費者的正常工作(反之同理)

解耦合在分布式系統中很常見,比如服務器的整個功能并不是由一個服務器全部完成,而是由多個服務器完成,每個服務器完成一部分功能,最后通過服務器之間的網絡通信,實現整個功能?

(2)緩沖機制

  1. 生產者突發大量請求時,隊列暫存數據,避免消費者過載崩潰。
  2. 消費者處理慢時,隊列累積任務,避免生產者因等待而阻塞。
  3. 可以將緩沖區作為“蓄水池”,協調速度差異
public class Demo_3 {public static void main(String[] args) {BlockingDeque<Integer> deque = new LinkedBlockingDeque<>(100);Thread t1 = new Thread(()->{while(true){try {Thread.sleep(300);Random random = new Random();int num = random.nextInt(101);System.out.println("生產數:"+ num);deque.put(num);} catch (InterruptedException e) {e.printStackTrace();}}},"生產者");t1.start();Thread t2 = new Thread(()->{while(true){try {Thread.sleep(500);int num = deque.take();System.out.println("消費數:"+num);} catch (InterruptedException e) {e.printStackTrace();}}},"消費者");t2.start();}
}

?

4.模擬生產者-消費者模型

核心:阻塞隊列的實現

將其看成一個循環隊列,其中兩個核心的方法:put()和take()

put():入隊列操作,如果隊列為滿則進入阻塞狀態,由take()進行喚醒

take():出隊列操作,如果隊列為空則進入阻塞狀態,由put()方法進行喚醒

在判斷是否需要進入阻塞狀態的時候,使用while語句,進行多次判斷,如果使用if語句相當于一錘定音,在阻塞的狀態下,可能會被notifyAll()喚醒,但是這時候隊列中的空間并不足夠(虛假喚醒),也有可能會出現連續喚醒的情況,最好的方式是再進行一次判斷

class MyBlockingQueue{Object A = new Object();int[] elems = null;int right ;int tail ;int usedSize;MyBlockingQueue(int capacity){elems = new int[capacity];}//注意鎖的位置//放入操作public void put(int elem) throws InterruptedException {synchronized (A){//如果滿,不能放出while (usedSize>=elems.length){A.wait();}//沒有滿,正常放入elems[tail++] = elem;if(tail>=elems.length){tail = 0;}usedSize++;A.notify();}}public int take() throws InterruptedException {synchronized (A){//如果為空,不能取出while (usedSize == 0){A.wait();}//不為空,正常取出int elem = elems[right++];//另一種寫法right = right%elems.length;usedSize--;A.notify();return elem;}}}

?點贊的寶子今晚自動觸發「躺贏錦鯉」buff!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/75070.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/75070.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/75070.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

終值定理的推導與理解

終值定理的推導與理解 終值定理是控制理論和信號處理中的一個重要工具&#xff0c;它通過頻域的拉普拉斯變換來分析時間域函數的最終穩態值。具體來說&#xff0c;終值定理提供了一個簡便的方法&#xff0c;利用 F ( s ) F(s) F(s)&#xff08; f ( t ) f(t) f(t) 的拉普拉斯…

每日c/c++題 備戰藍橋杯(二分答案模版)

在算法學習中&#xff0c;二分答案算法是一種非常高效且常用的技巧。它的核心思想是通過不斷縮小搜索范圍&#xff0c;逐步逼近目標答案。相比傳統的暴力搜索&#xff0c;二分答案算法的時間復雜度通常為 O(logn)&#xff0c;特別適合處理大規模數據的查找問題。 本文將詳細介…

NLP高頻面試題(二十六)——RAG的retriever模塊作用,原理和目前存在的挑戰

在自然語言處理領域&#xff0c;檢索增強生成&#xff08;Retrieval-Augmented Generation&#xff0c;簡稱RAG&#xff09;是一種將信息檢索與文本生成相結合的技術&#xff0c;旨在提升模型的回答準確性和信息豐富度。其中&#xff0c;Retriever在RAG架構中扮演著關鍵角色&am…

第30周Java分布式入門 分布式基礎

分布式基礎課程筆記 一、什么是分布式&#xff1f; 1. 權威定義 分布式系統定義為&#xff1a;“利用物理架構形成多個自治的處理元素&#xff0c;不共享主內存&#xff0c;通過發送消息合作”。 2. 核心解釋 物理架構與處理元素 &#x1f31f; 多臺獨立服務器/電腦&#x…

Vuex狀態管理

Vuex Vuex是一個專為Vue.js應用程序開發的狀態管理模式。它采用集中式管理應用的所有組件狀態&#xff0c;并以相應的規則保證狀態以一種可預測的方式發生變化。&#xff08;類似于在前端的數據庫&#xff0c;這里的數據存儲在內存當中&#xff09; 一、安裝并配置 在項目的…

從代碼學習深度學習 - 使用塊的網絡(VGG)PyTorch版

文章目錄 前言一、VGG網絡簡介1.1 VGG的核心特點1.2 VGG的典型結構1.3 優點與局限性1.4 本文的實現目標二、搭建VGG網絡2.1 數據準備2.2 定義VGG塊2.3 構建VGG網絡2.4 輔助工具2.4.1 計時器和累加器2.4.2 準確率計算2.4.3 可視化工具2.5 訓練模型2.6 運行實驗總結前言 深度學習…

Baklib激活企業知識管理新動能

Baklib核心技術架構解析 Baklib的底層架構以模塊化設計為核心&#xff0c;融合知識中臺的核心理念&#xff0c;通過分布式存儲引擎與智能語義分析系統構建三層技術體系。數據層采用多源異構數據接入協議&#xff0c;支持文檔、音視頻、代碼片段等非結構化數據的實時解析與分類…

小智機器人中的部分關鍵函數,FreeRTOS中`xEventGroupWaitBits`函數的詳細解析

以下是對FreeRTOS中xEventGroupWaitBits函數的詳細解析&#xff1a; 函數功能 xEventGroupWaitBits用于在事件組中等待指定的位被設置。它可以配置為等待任意一個位或所有位&#xff0c;并支持超時機制。 注意&#xff1a;該函數不能在中斷中調用。 函數原型 EventBits_t xEv…

關注分離(Separation of Concerns)在前端開發中的實踐演進:從 XMLHttpRequest 到 Fetch API

關注分離&#xff08;Separation of Concerns&#xff09;在前端開發中的實踐演進&#xff1a;從 XMLHttpRequest 到 Fetch API 一、關注分離的核心價值 關注分離&#xff08;SoC&#xff09;是軟件工程領域的重要設計原則&#xff0c;強調將系統分解為不同維度的功能模塊&am…

C之(16)scan-build與clang-tidy使用

C之(16)scan-build與clang-tidy使用 Author: Once Day Date: 2025年3月29日 一位熱衷于Linux學習和開發的菜鳥&#xff0c;試圖譜寫一場冒險之旅&#xff0c;也許終點只是一場白日夢… 漫漫長路&#xff0c;有人對你微笑過嘛… 全系列文章可參考專欄: Linux實踐記錄_Once_da…

在 Vue 項目中快速集成 Vant 組件庫

目錄 引言一、找到 src 下的App.js 寫入代碼。二、安裝Vant三、解決 polyfill 問題四、查看依賴五、配置webpack六、引入 Vant七、在組件中使用 Vant八、在瀏覽器中查看樣式總結 引言 在開發移動端 Vue 項目時&#xff0c;選擇一個高效、輕量且功能豐富的組件庫是提升開發效率…

“GPU 擠不動了?”——聊聊基于 GPU 的計算資源管理

“GPU 擠不動了?”——聊聊基于 GPU 的計算資源管理 作者:Echo_Wish “老板:為什么 GPU 服務器卡得跟 PPT 一樣?” “運維:我們任務隊列爆炸了,得優化資源管理!” 在 AI 訓練、深度學習、科學計算的場景下,GPU 計算資源已經成為香餑餑。但 GPU 服務器貴得離譜,一臺 A…

AI滲透測試:網絡安全的“黑魔法”還是“白魔法”?

引言&#xff1a;AI滲透測試&#xff0c;安全圈的“新魔法師” 想象一下&#xff0c;你是個網絡安全新手&#xff0c;手里攥著一堆工具&#xff0c;正準備硬著頭皮上陣。這時&#xff0c;AI蹦出來&#xff0c;拍著胸脯說&#xff1a;“別慌&#xff0c;我3秒掃完漏洞&#xff0…

(二)GEE基礎學習初探及案例詳解【20250330】

Google Earth Engine(GEE)是由谷歌公司開發的眾多應用之一。借助谷歌公司超強的服務器運算能力以及與NASA的合作關系&#xff0c;GEE平臺將Landsat、MODIS、Sentinel等可以公開獲取的遙感圖像數據存儲在谷歌的磁盤陣列中&#xff0c;使得GEE用戶可以方便的提取、調用和分析海量…

redhat認證是永久的嗎

?認證有效期 ?紅帽認證一般有效期為3年?&#xff08;如RHCSA、RHCE、RHCA等&#xff09;&#xff0c;從通過考試之日起計算。 ?例外&#xff1a;部分基礎或工程師認證&#xff08;如Red Hat Certified Engineer&#xff09;有效期為三年時間&#xff0c;以官方最新政策為準…

git --- cherry pick

git --- cherry pick cherry pick cherry pick Cherry Pick 是 Git 中的一個操作&#xff0c;它允許你選擇某個分支的某次&#xff08;或多次&#xff09;提交&#xff0c;并將其應用到當前分支&#xff0c;而不會合并整個分支的所有更改。 cherry pick 的作用 只提取某個特定的…

妙用《甄嬛傳》中的選妃來記憶概率論中的乘法公式

強烈推薦最近在看的不錯的B站概率論課程 《概率統計》正課&#xff0c;零廢話&#xff0c;超精講&#xff01;【孔祥仁】 《概率統計》正課&#xff0c;零廢話&#xff0c;超精講&#xff01;【孔祥仁】_嗶哩嗶哩_bilibili 其中概率論中的乘法公式&#xff0c;老師用了《甄嬛傳…

AI 的出現是否能替代 IT 從業者?

AI 的出現是否能替代 IT 從業者&#xff1f; AI 的快速發展正在深刻改變各行各業&#xff0c;IT 行業也不例外。然而&#xff0c;AI 并非完全替代 IT 從業者&#xff0c;而是與其形成互補關系。本文將從 AI 的優勢、IT 從業者的不可替代性、未來趨勢等方面&#xff0c;探討 AI…

【leetcode100】有效的括號

1、題目描述 給定一個只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判斷字符串是否有效。 有效字符串需滿足&#xff1a; 左括號必須用相同類型的右括號閉合。左括號必須以正確的順序閉合。每個右括號都有一個對應的…

為什么使用Flask + uWSGI + Nginx 部署服務?

概述 在Python開發的web應用中&#xff0c;我們通常能夠看到flask、uWSGI、Nginx出現在一起&#xff0c;他們之間的關系是什么&#xff1f;為什么總是被應用在一起&#xff1f; &#xfeff; 三者共同使用為了實現一個目的&#xff1a;客戶端向服務端發送數據請求&#xff0c;服…