【設計模式】單例模式|餓漢模式|懶漢模式|指令重排序

目錄

1.什么是單例模式?

2.如何保證單例?

3.兩種寫法

(1)餓漢模式(早創建)

?(2)懶漢模式(緩執行,可能不執行)

4.應用場景

🔥5.多線程中的單例模式

(1)加鎖

(2)雙重if

(3)volatie

6.指令重排序(小概率出現問題)

(1)什么是指令重排序?

?(2)以Instance = new SingletonLazy( );為例分析

(3)解決上述指令重排序問題

7.延伸:了解

🔥8.常見考察


1.什么是單例模式?

單例模式是最常見的設計模式之?

Q:啥是設計模式?

A:編程中典型場景的解決方案,設計模式好?象棋中的"棋譜".紅?當頭炮,???來跳.針對紅?的?些?法,??應招的時候有?些固定的套路.按照套路來?局勢就不會吃虧

單例模式即某個類在進程中又能有唯一實例

  • 有且只有一個對象,不會new出來多個對象,這樣的對象就是“單例”

2.如何保證單例?

(1)保證單例:instance只有唯一一個,初始化也只是執行一次的

  • 保證單例:instance只有唯一一個,初始化也只是執行一次的
    static修飾的,其實是“類屬性”,就是在“類對象”上的,每個類的類對象在JVM中只有一個,里面的靜態成員,只有一份
  • 后續需要使用這個類的實例,就可以直接通過getInstance來獲取已經new好的這個,而不是重新new

(2)?核心操作:private:禁止外部代碼來創建該類的實例

  • 怎么操作?類之外的代碼,嘗試new的時候,勢必就要調用構造方法,由于構造方法私有,無法調動,就會編譯出錯

3.兩種寫法

(1)餓漢模式(早創建)

唯一實例創建時機非常早,類似于餓了很久的人,?看到了吃的就趕緊開始吃(急迫)

package thread;//單例,餓漢模式
//唯一實例創建時機非常早,類似于餓了很久的人,看到了吃的就趕緊開始吃(急迫)
class Singleton {private static Singleton instance = new Singleton();public static Singleton getInstance() {return instance;}private Singleton() {}
}
public class Demo27 {public static void main(String[] args) {Singleton s1 = Singleton.getInstance();Singleton s2 = Singleton.getInstance();System.out.println(s1 == s2);//Singleton s3 = new Singleton();}
}

?(2)懶漢模式(緩執行,可能不執行)

懶是提高效率,節省開銷的體現 啥時候調用,就啥時候創建,如果不調用,就不創建了

package thread;//單例模式,懶漢模式的實現
class SingletonLazy {private static SingletonLazy instance = null;public static SingletonLazy getInstance() {if (instance == null) {instance = new SingletonLazy();}return instance;}private SingletonLazy (){}
}public class Demo28 {public static void main(String[] args) {SingletonLazy s1 = SingletonLazy.getInstance();SingletonLazy s2 = SingletonLazy.getInstance();System.out.println(s1 == s2);//SingletonLazy s3 = new SingletonLazy();}
}

Q:如果有多個線程同時調用getInstance,是否會產生線程安全問題?

A

  • 餓漢模式不存在線程安全問題,而懶漢模式存在
    ?
  • 餓漢模式:實例早就有了,每個線程getInstance,就是讀取上面的靜態變量,多個線程讀取同一個變量,是線程安全的
  • 懶漢模式:instance = new SingletonLazy,賦值操作就是修改,而且操作不是原子的,肯定就是線程不安全的

圖解:懶漢模式:非原子性操作

  • 還可能存在其他執行順序,t2再次new,可能創建多個實例
  • instance只是一個引用,instance中地址指向的那個對象可能就是一個大對象,上述代碼會出現覆蓋,第二個對象的地址覆蓋了第一個,進一步第一個對象沒有引用指向了,就會被GC回收(但是這個創建時間的開銷,是客觀存在的)

4.應用場景

(1)寫的服務器,要從硬盤上加載100G的數據到內存(加載到若干個哈希表中),肯定要寫一個類,封裝上述加載操作,并且獲取一些獲取、處理數據的業務邏輯

  • 代碼中的有些對象,本身就不應該是多個實例的,從業務角度就應該是單個實例
    一個實例就管理100G的內存數據
  • 搞多個實例,就是N*100G的內存數據,機器肯定吃不消,沒必要

(2)MySQL的配置文件,專門管理配置,需要加載配置數據到內存中提供其他代碼使用,這樣的類也是單例的

  • 如果是多個實例,就存儲了多份數據,如果一樣還罷了,如果不一樣,以哪個為準?

🔥5.多線程中的單例模式

懶漢模式的線程安全問題如何解決呢?

(1)加鎖

Q:這樣加鎖線程就安全了嗎?

public static SingletonLazy getInstance() {if (instance == null) {synchronized (locker) {instance = new SingletonLazy();}}return instance;}

A:?沒有,t2拿到鎖后,還在直接執行new操作

圖解:

應該把if和new操作打包成一個原子操作

public static SingletonLazy getInstance() {synchronized (locker) {if(instance == null) {instance = new SingletonLazy();}}return instance;}

(2)雙重if

?加鎖之后的問題Q:懶漢模式只有最開始調用getInstance會存在線程安全問題,一旦把實例創建好了,后續再調用,就是只讀操作,就不存在線程安全問題了,但是上述代碼,只要一調用getInstance方法,就需要先加鎖,再執行后續操作(后續沒有線程安全問題,還要加鎖,有開銷

真正的解決A:再加一層判斷

連續兩次一樣的條件判斷(單線程中無意義,但是多線程含義就不一樣)

第一層if:判定是否要加鎖(new之前要加鎖,new之后就不用加了)

第二層if:判斷是否要創建對象

?指令級理解

?t2拿到鎖,這個時候instance已經被t1修改了

(3)volatie

private static volatile SingletonLazy instance = null;

為了保證第一次線程修改,后續線程一定會讀到,加上volatile【避免內存可見性+指令重排序問題

綜上:懶漢模式的線程安全版

//單例模式,懶漢模式的實現
class SingletonLazy {private static volatile SingletonLazy instance = null;private static Object locker = new Object();public static SingletonLazy getInstance() {if (instance == null) {synchronized (locker) {if(instance == null) {instance = new SingletonLazy();}}}return instance;}private SingletonLazy (){}
}

6.指令重排序(小概率出現問題)

編譯器的優化策略有很多: ?

把讀內存優化到讀寄存器,指令重排序,循環展開,條件分支預測...

(1)什么是指令重排序?

編譯器會在保證邏輯是等價的情況下,調整二進制指令的執行順序,從而提高效率

  • 正常來說寫的代碼,最終會編譯成為一系列的二進制指令,CPU會按照順序,一條一條執行

?(2)以Instance = new SingletonLazy( );為例分析

  • 這行代碼可以細分為三個步驟
    • 申請內存空間
    • 調用構造方法(對內存空間進行優化)
    • 把此時內存空間的地址,賦值給Instance引用
  • 在指令重排序的策略下,上述執行的過程,不一定是123,也可能是132(但是1一定先執行)
    • 132這樣的執行順序,就可能存在線程安全問題
  • 指令級理解

    • 3一旦執行完,就意味著instance就非null,但是指向的對象其實是一個未初始化的對象(里面的成員都是0)
    • 執行到t2的時候,instance已經非null了,這里的條件無法進行,直接返回未初始完畢的instance
    • 后續如果t2中還有其他邏輯,就會對未初始完畢的對象進行操作,這樣存在嚴重的問題

(3)解決上述指令重排序問題

  • 加上volatile,主要是針對某個對象的讀寫過程中,不會出現重排序
    • 很多地方都能重排序,但是只是針對這一過程中
    • 這樣t2線程讀到的數據,一定是t1已經構造完畢的完整對象了(一定是123執行的對象)

7.延伸:了解

(1)單例模式要確保反射下安全,即使動用反射也無法破壞單例特性

(2)單例模式要確保序列化下安全,即使動用Java標準庫的序列化機制,也無法破壞單例特性

  • enum類型的實例天然支持序列化和反序列化
  • 序列化:把對象轉為二進制字符串

🔥8.常見考察

(1)為什么說餓漢式單例天生就是線程安全的?

實例早就有了,每個線程getInstance,就是讀取上面的靜態變量,多個線程讀取同一個變量,是線程安全的

(2)傳統的懶漢式單例為什么是非線程安全的?

instance = new SingletonLazy,賦值操作就是修改,而且操作不是原子的,肯定就是線程不安全的

(3)怎么修改傳統的懶漢式單例,使其線程變得安全?

1.線程不安全的版本

2.加鎖版本

3.加上雙重if

4.最后加上volatile

(4)線程安全的單例的實現還有哪些,怎么實現?

靜態內部類單例

public class StaticInnerClassSingleton {private StaticInnerClassSingleton() {}private static class SingletonHolder {private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();}public static StaticInnerClassSingleton getInstance() {return SingletonHolder.INSTANCE;}
}

?靜態內部類在外部類加載時不會被加載,只有在調用?getInstance?方法時才會加載類,從而創建實例。類加載過程是線程安全的,所以這種方式實現了線程安全的單例,并且具有延遲加載的特性

(5)雙重檢查模式、Volatile關鍵字在單例模式中的應用

1.雙重檢查模式:

第一層if:判定是否要加鎖(new之前要加鎖,new之后就不用加了)

第二層if:判斷是否要創建對象

2.Volatile關鍵字:避免內存可見性+指令重排序問題

(6)ThreadLocal在單例模式中的應用

public class ThreadLocalSingleton {private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance = new ThreadLocal<ThreadLocalSingleton>() {@Overrideprotected ThreadLocalSingleton initialValue() {return new ThreadLocalSingleton();}};private ThreadLocalSingleton() {}public static ThreadLocalSingleton getInstance() {return threadLocalInstance.get();}
}

ThreadLocal 會為每個使用該實例的線程都提供一個獨立的副本,每個線程都可以獨立地改變自己的副本,而不會影響其他線程所對應的副本。在單例模式中使用 ThreadLocal,可以保證每個線程都有自己的單例實例,適用于需要在每個線程中維護單例狀態的場景?

(7)枚舉式單例

public enum EnumSingleton {INSTANCE;public void doSomething() {System.out.println("Doing something...");}
}

?枚舉式單例是實現單例模式的最佳方式之一。它是線程安全的,因為枚舉類型的實例創建是由 JVM 保證線程安全的。而且枚舉類型可以防止反序列化和反射攻擊,因為 Java 規范中規定,枚舉類型的 clone()、readObject()、readResolve() 等方法都不會破壞單例的唯一性。使用時可以直接通過 EnumSingleton.INSTANCE 來獲取單例實例

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

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

相關文章

RocketMQ順序消費機制

RocketMQ的順序消費機制通過生產端和消費端的協同設計實現&#xff0c;其核心在于局部順序性&#xff0c;即保證同一隊列&#xff08;MessageQueue&#xff09;內的消息嚴格按發送順序消費。以下是詳細機制解析及關鍵源碼實現&#xff1a; 一、順序消費的核心機制 1. 生產端路…

【JavaEE】-- 多線程(初階)4

文章目錄 8.多線程案例8.1 單例模式8.1.1 餓漢模式8.1.2 懶漢模式 8.2 阻塞隊列8.2.1 什么是阻塞隊列8.2.2 生產者消費者模型8.2.3 標準庫中的阻塞隊列8.2.4 阻塞隊列的應用場景8.2.4.1 消息隊列 8.2.5 異步操作8.2.5 自定義實現阻塞隊列8.2.6 阻塞隊列--生產者消費者模型 8.3 …

【C++設計模式】第四篇:建造者模式(Builder)

注意&#xff1a;復現代碼時&#xff0c;確保 VS2022 使用 C17/20 標準以支持現代特性。 分步驟構造復雜對象&#xff0c;實現靈活裝配 1. 模式定義與用途 核心目標&#xff1a;將復雜對象的構建過程分離&#xff0c;使得同樣的構建步驟可以創建不同的表示形式。 常見場景&am…

vuex中的state是響應式的嗎?

在 Vue.js 中&#xff0c;Vuex 的 state 是響應式的。這意味著當你更改 state 中的數據時&#xff0c;依賴于這些數據的 Vue 組件會自動更新。這是通過 Vue 的響應式系統實現的&#xff0c;該系統使用了 ES6 的 Proxy 對象來監聽數據的變化。 當你在 Vuex 中定義了一個 state …

若依框架中的崗位與角色詳解

若依框架中的崗位與角色詳解 一、核心概念與定位 崗位&#xff08;Post&#xff09; 業務職能導向&#xff1a;崗位是用戶在組織架構中的職務標識&#xff08;如“開發人員”“項目經理”&#xff09;&#xff0c;用于描述工作職責而非直接控制權限。崗位與部門關聯&#xff…

SQL經典常用查詢語句

1. 基礎查詢語句 1.1 查詢表中所有數據 在SQL中&#xff0c;查詢表中所有數據是最基本的操作之一。通過使用SELECT * FROM table_name;語句&#xff0c;可以獲取指定表中的所有記錄和列。例如&#xff0c;假設有一個名為employees的表&#xff0c;包含員工的基本信息&#xf…

EP 架構:未來主流方向還是特定場景最優解?

DeepSeek MoE架構采用跨節點專家并行&#xff08;EP&#xff09;架構&#xff0c;在提升推理系統性能方面展現出巨大潛力。這一架構在發展進程中也面臨諸多挑戰&#xff0c;其未來究竟是會成為行業的主流方向&#xff0c;還是僅適用于特定場景&#xff0c;成為特定領域的最優解…

[密碼學實戰]Java實現國密(SM2)密鑰協商詳解:原理、代碼與實踐

一、代碼運行結果 二、國密算法與密鑰協商背景 2.1 什么是國密算法&#xff1f; 國密算法是由中國國家密碼管理局制定的商用密碼標準&#xff0c;包括&#xff1a; SM2&#xff1a;橢圓曲線公鑰密碼算法&#xff08;非對稱加密/簽名/密鑰協商&#xff09;SM3&#xff1a;密碼…

動漫短劇開發公司,短劇小程序搭建快速上線

在當今快節奏的生活里&#xff0c;人們的娛樂方式愈發多元&#xff0c;而動漫短劇作為新興娛樂形式&#xff0c;正以獨特魅力迅速崛起&#xff0c;成為娛樂市場的耀眼新星。近年來&#xff0c;動漫短劇市場呈爆發式增長&#xff0c;吸引眾多創作者與觀眾目光。 從市場規模來看…

第四十五:創建一個vue 的程序

html <div id"app">{{ msg }}<h2>{{ web.title }}</h2><h3>{{ web.url }}</h3> </div> js /*<div id"app"></div> 指定一個 id 為 app 的 div 元素{{ }} 插值表達式, 可以將 Vue 實例中定義的數據在視圖…

docer swarm集群部署springboot項目

1.準備兩臺服務器&#xff0c;安裝好docker、docker-compose 因為用到了docker倉庫&#xff0c;安裝harbor,可以從github下載離線安裝包 2. 我這邊用到了gitlab-ci,整體流程也都差不多 1&#xff09;打包mvn clean install 2&#xff09;打鏡像 docker-compose -f docker-compo…

Python測試框架Pytest的參數化

上篇博文介紹過&#xff0c;Pytest是目前比較成熟功能齊全的測試框架&#xff0c;使用率肯定也不斷攀升。 在實際工作中&#xff0c;許多測試用例都是類似的重復&#xff0c;一個個寫最后代碼會顯得很冗余。這里&#xff0c;我們來了解一下pytest.mark.parametrize裝飾器&…

開發博客系統

前言 準備工作 數據庫表分為實體表和關系表 第一&#xff0c;建數據庫表 然后導入前端頁面 創建公共模塊 就是統一返回值&#xff0c;異常那些東西 自己造一個自定義異常 普通類 mapper 獲取全部博客 我們只需要返回id&#xff0c;title&#xff0c;content&#xff0c;us…

【Spring Boot 應用開發】-05 命令行參數

Spring Boot 常用命令行參數 Spring Boot 支持多種命令行參數&#xff0c;這些參數可以在啟動應用時通過命令行直接傳遞。以下是一些常用的命令行參數及其詳細說明&#xff1a; 1. 基本配置參數 --server.port端口號 指定應用程序運行的HTTP端口&#xff0c;默認為8080。 jav…

20250304學習記錄

第一部分&#xff0c;先來了解一下各種論文期刊吧&#xff0c;畢竟也是這把歲數了&#xff0c;還什么都不懂呢 國際期刊&#xff1a; EI收集的主要有兩種&#xff0c; JA&#xff1a;EI源刊 CA&#xff1a;EI會議 CPCI也叫 ISTP 常說的SCI分區是指&#xff0c;JCR的一區、…

2024 年 MySQL 8.0.40 安裝配置、Workbench漢化教程最簡易(保姆級)

首先到官網上下載安裝包&#xff1a;http://www.mysql.com 點擊下載&#xff0c;拉到最下面&#xff0c;點擊社區版下載 windows用戶點擊下面適用于windows的安裝程序 點擊下載&#xff0c;網絡條件好可以點第一個&#xff0c;怕下著下著斷了點第二個離線下載 雙擊下載好的安裝…

網絡安全檢查漏洞內容回復 網絡安全的漏洞

網絡安全的核心目標是保障業務系統的可持續性和數據的安全性&#xff0c;而這兩點的主要威脅來自于蠕蟲的暴發、黑客的攻擊、拒絕服務攻擊、木馬。蠕蟲、黑客攻擊問題都和漏洞緊密聯系在一起&#xff0c;一旦有重大安全漏洞出現&#xff0c;整個互聯網就會面臨一次重大挑戰。雖…

汽車智能鑰匙中PKE低頻天線的作用

PKE&#xff08;Passive Keyless Entry&#xff09;即被動式無鑰匙進入系統&#xff0c;汽車智能鑰匙中PKE低頻天線在現代汽車的智能功能和安全保障方面發揮著關鍵作用&#xff0c;以下是其具體作用&#xff1a; 信號交互與身份認證 低頻信號接收&#xff1a;當車主靠近車輛時…

uiautomatorviewer定位元素報Unexpected ... UI hierarchy

發現問題 借鑒博客 Unexpected error while obtaining UI hierarchy android app UI自動化-元素定位輔助工具 Unexpected error while obtaining UI hierarchy&#xff1a;使用uiautomatorviewer定位元素報錯 最近在做安卓自動化,安卓自動化主要工作之一就是獲取UI樹 app端獲…

通俗的方式解釋“零錢兌換”問題

“零錢兌換”是一道經典的算法題目&#xff0c;其主要問題是&#xff1a;給定不同面額的硬幣和一個總金額&#xff0c;求出湊成總金額所需的最少硬幣個數。如果沒有任何一種硬幣組合能組成總金額&#xff0c;返回-1。 解題思路 動態規劃&#xff1a;使用動態規劃是解決零錢兌…