【多線程案例】:單例模式

多線程案例

  • 8.1 單例模式
    • 餓漢模式
    • 懶漢模式
      • 懶漢模式-單線程版
      • 懶漢模式-多線程版
      • 懶漢模式-多線程版(改進)

8.1 單例模式

單個實例. 在一個 java 進程中, 要求指定的類,只能有唯–個實例。(嘗試 new 多個實例的時候, 就會直接編譯報錯)

單例模式是校招中最常考的設計模式之?.

啥是設計模式?
設計模式好?象棋中的 “棋譜”. 紅?當頭炮, ???來跳. 針對紅?的?些?法, ??應招的時候有?些固定的套路. 按照套路來?局勢就不會吃虧.
軟件開發中也有很多常?的 “問題場景”. 針對這些問題場景, ?佬們總結出了?些固定的套路. 按照這個套路來實現代碼, 也不會吃虧.

單例模式能保證某個類在程序中只存在唯??份實例, ?不會創建出多個實例.
這?點在很多場景上都需要. ?如 JDBC 中的 DataSource 實例就只需要?個.

什么場景適合使用單例模式?
代碼中的有些對象,本身就不應該是有多個實例的.從業務角度就應該是單個實例.
在這里插入圖片描述

單例模式具體的實現?式有很多. 最常?的是 “餓漢” 和 “懶漢” 兩種.

餓漢模式

類加載的同時, 創建實例.

class Singleton {private static Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}

在這個類被加載的時候,就會初始化這個 靜態成員。實例創建的時機非常早,就使用"餓漢!
在這里插入圖片描述
萬一,其他代碼又 new 了這個類的實例咋辦呢?需要禁止外部代碼來創建該類的實例
在這里插入圖片描述
雖然構造方法是 private,但是能否在類外面通過 反射 拿到私有構造方法創建實例??
原則上來說,可以做到。但是, 實際開發中, 反射不敢亂用的!!!反射屬于非常規的編程,特殊場景下的特殊解決方案!!!! 使用反射要付出很大的代價(會嚴重影響代碼的可讀性和封裝性)
類似于,通常情況下,你肯定沒法直接闖入別人家里。但是, 你不能進, 不代表jc 蜀黍不能進,如果jc 蜀黍到處亂闖,當然也是不行的
代碼中隨便濫用反射,是非常糟糕的~~

懶漢模式

懶漢模式-單線程版

類加載的時候不創建實例. 第?次使?的時候才創建實例.

class Singleton {private static Singleton instance = null;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

在計算機中,懶 的思想,就非常有意義
在這里插入圖片描述
如果代碼中存在多個單例類,使用餓漢模式,就會導致這些實例都是在程序啟動的時候扎堆的創建的.可能把程序啟動時間拖慢.
如果是懶漢模式,啥時候首次調用, 調用時機是分散的. 化整為零, 用戶不太容易感知到"卡頓

如果是首次調用 getlnstance, 那么此時 instance 引用為 null,就會進入 if 條件,從而把實例創建出來,如果是后續再次調用 getlnstance, 由于 instance 已經不再是 null,此時不會進入if, 直接返回之前創建好的引用了。這樣設定,仍然可以保證,該類的實例是唯一一個。與此同時,創建實例的時機就不是程序驅動時了,而是第一次調用getlnstance的時候
這個操作的執行時機就看你程序的實際需求。大概率要比餓漢這種方式要晚一些,甚至有可能整個程序壓根用不到這個方法,也就把創建的操作給省下了

有的程序, 可能是根據一定的條件,來決定是否要進行某個操作,進一步的來決定創建某個實例
比如,肯德基有個操作“瘋狂星期四”,對于 肯德基 點餐系統來說,就可以判定今天星期幾。如果是星期四,才加載 瘋狂星期四 相關的邏輯和數據,如果不是星期四,就不用加載了(節省了一定的開銷)

懶漢模式-多線程版

上述的代碼,餓漢模式和懶漢模式,是否是線程安全的?? 如果在多個線程中, 并發的調用 getlnstance, 這兩個代碼是否是線程安全的呢??
餓漢: getlnstance 直接返回 Instance 實例. 這個操作本質上就是"讀操作"。多個線程讀取同一個變量,是線程安全的!!
懶漢: 線程不安全,在多線程環境下可能會創建出多個實例!!在懶漢模式中,代碼有讀也有寫,如果 t1 和 t2 按照下列順序來執行,就會出現問題!!在這里插入圖片描述

上?的懶漢模式的實現是線程不安全的.
線程安全問題發?在?次創建實例時. 如果在多個線程中同時調? getInstance ?法, 就可能導致創建出多個實例.
?旦實例已經創建好了, 后?再多線程環境調? getInstance 就不再有線程安全問題了(不再修改instance 了)

加上 synchronized 可以改善這?的線程安全問題.

class Singleton {private static Singleton instance = null;private Singleton() {}public synchronized static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

懶漢模式-多線程版(改進)

多線程代碼, 其實是非常復雜的,代碼稍微變換一點,結論就截然不同!!
因此可千萬不要以為,代碼中寫了 synchronized 就一定線程安全,不寫 synchronized 就一定線程不安全!!!
一定要具體問題具體分析.要分析這個代碼在各種調度執行順序下可能的情況,確保每個情況都是正確的!!

此處要想讓代碼執行正確,其實是需要把 if 和 new 兩個操作,打包成一個原子的!!
更加合理的做法,應該是把 synchronized 套到if 外頭~~
在這里插入圖片描述

但上述代碼仍然存在問題~~

效率非常低!!!
如果 Instance 已經創建過了,此時后續再調用 getlnstance 就都是直接返回 Instance 實例了(此處的操作就是純粹的讀操作了,也就不會有線程安全問題了)
此時,針對這個已經沒有線程安全問題的代碼,仍然是每次調用都先加鎖再解鎖,此時,效率就非常低了!!!加鎖就意味著可能會產生阻塞,一旦線程阻塞,啥時候能解除,就不知道了(你可以認為,只要一個代碼里加鎖了,基本就注定和“高性能"無緣)
在需要加鎖的時候才加鎖,不該加鎖,不能隨便亂加。所以除了 StringBuffer 還提供 StringBuilder, 除了 Vector 還提供 ArrayList
在這里插入圖片描述

這個代碼仍然有點問題~~

指令重排序,引起的線程安全問題
指令重排序,也是編譯器優化的一種方式,調整原有代碼的執行順序,保證邏輯不變的前提下,提高程序的效率
instance = new singletonLazy();
這行代碼,其實可以拆成三個大的步驟,(不是三個指令)
1.申請一段內存空間
2.在這個內存上調用構造方法,創建出這個實例
3.把這個內存地址賦值給 |nstance 引用變量
正常情況下,上述代碼是按照 123 的順序來執行的,但是編譯器也可能會優化成132的順序來執行,無論是123 還是132在單線程下都是可以的~~
1 就相當于是你買了個房子,2 就相當于給房子裝修,3 就相當于你拿到房子的鑰匙。123 拿到鑰匙之后,就得到了裝修好的房子. 稱為"精裝房",132你先拿鑰匙,然后自己負責裝修.稱為"毛壞房"。如果你出去買房子,這兩種情況都會存在!!!
但是, 如果是在多線程下,指令重排序,就可能引入問題了!!如果你出去買房子,這兩種情況都會存在!!!
t1 按照132 的方式來執行這里的 new 操作
在這里插入圖片描述
上述代碼中,由于 t1 線程執行完13之后,調度走,此時 instance 指向的是一個 非 null 的,但是未初始化的對象。此時 t2 線程判定 instance == null 不成立,就會直接 return.如果 t2 繼續使用 instance 里面的屬性或者方法,就會出現問題(此時這里的屬性都是未初始化的"全 0"值). 就可能會引起代碼的邏輯出現問題.
解決上述問題,核心思路, 還是 volatile
volatile 有兩個功能
1.保證內存可見性,每次訪問變量必須都要重新讀取內存,而不會優化到寄存器/緩存中
2.禁止指令重排序.針對這個唄 volatile 修飾的變量的讀寫操作相關指令,是不能被重排序的!!
在這里插入圖片描述
在這里插入圖片描述

以下代碼在加鎖的基礎上, 做出了進?步改動:
? 使?雙重 if 判定, 降低鎖競爭的頻率.
? 給 instance 加上了 volatile.

class Singleton {
private static volatile Singleton instance = null;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

這個代碼是一個經典高頻面試題,非常重要,咱們同學們最近這幾年秋招也會經常遇到這個問題~~
這個題并不簡單。加上這三個點,怎么加,容易答上來,為啥要這么加,每個地方解決的什么問題,要想給面試官解釋清楚,沒那么容易的!!
(1)寫博客,提前梳理好你都要說啥。
(2)給面試官講的過程中,一定要多畫圖
線下面試,可以自帶紙筆;線上面試,一般面試系統也會支持畫圖功能,可以共享屏幕。有的面試系統 牛客網面試系統,自身就支持畫圖,包括 騰訊會議,也支持畫圖
多去畫!!!
目前來看線上面試越來越多,越是好的公司,越是線上面試
面試中考察的方法非常簡單:
讓你現場寫一個單例模式的代碼
這個代碼咋寫?直接就寫成現在這個模樣嘛??
正確的寫法:
1.先寫一個不帶線程安全的單例模式
2.思索片刻, 線程不安全,把鎖加上
3.再次思索片刻,加上 if(雙重 if)
4. 再次思考片刻, 加上 volatile
意味著這個題不是你提前準備好,是你現場想出來的,面試官就會覺得,你這邊很可能沒有準備過/很久之前看的,即使如此,能夠通過已經掌握的知識,推理出一些結論
一次寫出最終版本,再面試官眼里,他覺得這個問題,你正好準備過,此時說明這個題目就考察不出來啥,這題不算,談下一話題(面試的時候,大部分面試官,看到你的回答有問題的時候,都會進一步去問的)
人生如戲,全靠演技
把問題引導到你自己擅長的角度,把控整個面試的節奏~~

理解雙重 if 判定 / volatile:
加鎖 / 解鎖是?件開銷?較?的事情. ?懶漢模式的線程不安全只是發?在?次創建實例的時候. 因此后續使?的時候, 不必再進?加鎖了.
外層的 if 就是判定下看當前是否已經把 instance 實例創建出來了.
同時為了避免 “內存可?性” 導致讀取的 instance 出現偏差, 于是補充上 volatile .
當多線程?次調? getInstance, ?家可能都發現 instance 為 null, 于是?繼續往下執?來競爭鎖, 其中競爭成功的線程, 再完成創建實例的操作.
當這個實例創建完了之后, 其他競爭到鎖的線程就被?層 if 擋住了. 也就不會繼續創建其他實例.

  1. 有三個線程, 開始執? getInstance , 通過外層的 if (instance == null) 知道了實例還沒有創建的消息. 于是開始競爭同?把鎖.
    在這里插入圖片描述
  2. 其中線程1 率先獲取到鎖, 此時線程1 通過?層的 if (instance == null) 進?步確認實例是否已經創建. 如果沒創建, 就把這個實例創建出來.
    在這里插入圖片描述
  3. 當線程1 釋放鎖之后, 線程2 和 線程3 也拿到鎖, 也通過?層的 if (instance == null) 來確認實例是否已經創建, 發現實例已經創建出來了, 就不再創建了
    在這里插入圖片描述
  4. 后續的線程, 不必加鎖, 直接就通過外層 if (instance == null) 就知道實例已經創建了,從?不再嘗試獲取鎖了. 降低了開銷.
    在這里插入圖片描述

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

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

相關文章

【Python/Pytorch】-- 貝葉斯定理

文章目錄 文章目錄01 貝葉斯定理的理解02 在MRI重建領域應用01 貝葉斯定理的理解 貝葉斯定理的基本公式:P(A|B)P(B|A)*P(A) / P(B) 首先是如何理解這個公式? 在B事件發生的條件下,A發生的概率 P(A|B) 在B事件發生的條件下,A和B同…

子網掩碼的隱形陷阱:為何能ping通卻無法HTTPS訪問

問題現象深度解析在近期企業網絡維護中,運維團隊發現一個具有教學意義的典型案例:某臺部署在10.165.111.0/24網段的業務服務器(10.165.111.71)可以成功ping通目標中間件主機(10.165.110.11),但通過HTTPS協議訪問https:…

【ArcGIS】如何編輯圖層的屬性表

GIS按屬性選擇后刪除所選項呈現灰色_arcgis刪除字段灰色-CSDN博客

大數據各組件flume,datax,presto,DolphinScheduler,findBI在大數據數倉架構中的作用和功能。

一、數據倉庫核心價值鋪墊在講具體技術前,先明確數據倉庫(Data Warehouse,簡稱數倉) 的核心作用: 數據倉庫是 “整合企業多源數據、按業務主題組織、支持決策分析” 的結構化數據存儲體系,核心價值是打破數…

React From表單使用Formik和yup進行校驗

一、Formik的使用 官方文檔地址:https://formik.org/docs/tutorial#validation 首先安裝依賴 yarn add formik2.導入并初始化 import { useFormik } from formik; initialValues:初始化 輸入框的密碼和賬號 onSubmit:當點擊提交按鈕時&am…

netty-scoket.io路徑配置

1、服務端代碼 package com.yh.service.socket;import com.corundumstudio.socketio.SocketIOServer; import com.corundumstudio.socketio.store.RedissonStoreFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory…

20250910榮品RD-RK3588-MID開發板在Android13系統下解決點卡迪的屏閃屏的問題

20250910榮品RD-RK3588-MID開發板在Android13系統下解決點卡迪的屏閃屏的問題 2025/9/5 15:44緣起:榮品RD-RK3588-MID開發板在Android13系統下解決點卡迪的屏。 按 POWER按鍵 關機之后,2s之內再次短按 POWER按鍵,開機之后屏會抖動。 2s后短按…

正態分布 - 計算 Z-Score 的 無偏估計

正態分布 - 計算 Z-Score 的 無偏估計 flyfish Z-Score公式與計算步驟 1 公式(樣本Z-Score) 實際應用中,我們幾乎不知道“總體均值/標準差”,所以常用樣本數據計算: zixi?xˉsz_i \frac{x_i - \bar{x}}{s}zi?sxi??…

ai生成文章,流式傳輸(uniapp,微信小程序)

1.環境nutui-uniappvue3tsunocss2.功能源碼包含ai生成邏輯&#xff0c;內容生成實時打字機功能&#xff0c;ai數據處理等<script setup lang"ts"> import {queryAIParams, } from /api/pagesA import { submitFn } from /api/aiimport Navbar from /component…

Linux設備內存不足如何處理

[rootlocalhost ~]# free -mtotal used free shared buff/cache available Mem: 31208 14317 1280 1551 15610 14657 Swap: 15927 2781 13146 [rootlocalhost ~]#從 free -m 輸出來看&…

中間件八股

文章目錄RedisRedis為什么快&#xff1f;Redis Redis為什么快&#xff1f; 首先它是內存數據庫&#xff0c;所有數據直接操作內存而非磁盤&#xff0c;避免了 I/O 瓶頸&#xff1b;其次采用單線程模型&#xff0c;消除了多線程切換的開銷&#xff0c;同時通過非阻塞 I/O 多路…

【參數詳解與使用指南】PyTorch MNIST數據集加載

# 加載MNIST數據集 train_dataset datasets.MNIST(root./data, trainTrue, downloadTrue, transformtransform) # 下載訓練集 test_dataset datasets.MNIST(root./data, trainFalse, downloadTrue, transformtransform) # 下載測試集在深度學習入門過程中&#xff0c;MNIST手…

閉包面試題

閉包&#xff08;Closure&#xff09; 是指一個函數能夠記住并訪問其詞法作用域&#xff08;定義時的作用域&#xff09;&#xff0c;即使該函數在其詞法作用域之外執行。一、通俗理解&#xff08;面試可這樣開頭&#xff09;&#xff1a;> 閉包就是一個函數“記住”了它出生…

WebSocket 雙向通信實戰:SCADA 移動端實時操控響應優化

引言&#xff1a;SCADA 移動端的 “延遲煩惱” 與破局之道在電力調度、水廠監控、智能制造等場景中&#xff0c;SCADA 系統&#xff08;數據采集與監視控制系統&#xff09;是當之無愧的 “工業指揮官”—— 它能實時采集設備運行數據&#xff08;如電網負荷、水泵壓力、機床轉…

SafeEar:浙大和清華聯合推出的AI音頻偽造檢測框架,錯誤率低至2.02%

本文轉載自&#xff1a;https://www.hello123.com/safeear ** 一、&#x1f512; SafeEar&#xff1a;你的聲音 “防火墻”&#xff0c;讓 AI 偽造音頻無所遁形 擔心自己的聲音被 AI 模仿甚至偽造&#xff1f;SafeEar就是來幫你解決這個難題的&#xff01;它是由浙江大學和清…

uni-app iOS 日志與崩潰分析全流程 多工具協作的實戰指南

在 uni-app 跨平臺開發中&#xff0c;iOS 應用的日志與崩潰分析往往是開發者最頭疼的問題。 日志分散&#xff1a;uni-app 的 JS 日志、原生插件日志、系統日志分布在不同位置&#xff1b;崩潰難復現&#xff1a;用戶反饋的崩潰往往無法在開發機還原&#xff1b;符號化復雜&…

CSS定義網格的列模板grid-template-columns什么意思,為什么要用這么復雜的單詞

這個詞確實看起來復雜&#xff0c;但其實很好理解。讓我來拆解一下&#xff1a;單詞分解grid-template-columns grid - 網格template - 模板columns - 列連起來就是&#xff1a;網格模板列 → 定義網格的列模板為什么要用這么長的單詞&#xff1f;語義明確&#xff1a;長單詞能…

Umi-OCR:Windows7和Linux上可免費離線使用的OCR應用!

工具介紹 Umi-OCR 是一款免費、開源的離線OCR軟件&#xff0c;主要由作者 hiroi-sora 用業余時間在開發和維護。 Umi-OCR 內置多國語言庫&#xff0c;支持截屏/批量導入圖片&#xff0c;PDF文檔識別&#xff0c;排除水印/頁眉頁腳以及二維碼的掃描/生成。 適用平臺&#xff1…

30 分鐘讓 AI 開口查訂單:React-Native + Coze 全鏈路語音對話落地指南

一、前言&#xff1a;為什么你需要“可說話、能查庫”的 AI&#xff1f; 聊天機器人在 2025 已不新鮮&#xff0c;但**“張嘴就能查詢私有業務數據”**的端到端方案依然踩坑無數&#xff1a; ASR/TTS 選型多、SDK 難對齊大模型與內部 API 安全打通RN 端流式渲染 音頻播放并發…

玄機--應急響應--webshell查殺

靶場連接1.黑客webshell里面的flag flag{xxxxx-xxxx-xxxx-xxxx-xxxx}使用命令查找特殊文件//搜索目錄下適配當前應用的網頁文件&#xff0c;查看內容是否有Webshell特征 find ./ type f -name "*.jsp" -exec grep -l "exec(" {} \; find ./ type f -name &…