線程P5 | 單例模式[線程安全版]~懶漢 + 餓漢

什么是單例模式?

在我們正式講解單例模式之前,沒有了解過的小伙伴可能會有疑問...到底啥叫單例模式??其實單例模式呢,是我們設計模式中的一種,所謂的設計模式,你可以把它理解為一個模板,也就是你在實現某種業務的時候,選擇適配的設計模式,根據這個模板來改你對應的業務代碼

Java設計模式是解決特定軟件設計問題的經典、可復用的方案模板,分為創建型、結構型和行為型三大類,幫助開發者編寫更靈活、可維護的代碼。

那么我們的單例模式呢,指的是實例對象只會被創建一次這樣的設計模式~~

為了實現這樣的要求,單例模式中又有兩種形式:餓漢模式懶漢模式,接下來我們將會為大家一一介紹這兩種模式

餓漢模式

什么是餓漢模式

所謂的餓漢模式,其實指的是實例從代碼剛開始運行的時候就已經創建好的模式,那實例就處在一個等著被調用的狀態,所以就一直餓著來等待資源~~因此就叫做餓漢模式啦

餓漢模式實現

class Singleton {   //餓漢模式private static Singleton instance = new Singleton();public static Singleton getInstance() {return instance;}private Singleton() {}}
public class Demo19 {public static void main(String[] args) {Singleton t1 = Singleton.getInstance();Singleton t2 = Singleton.getInstance();boolean res = t1 == t2;System.out.println(res);}
}

如上,我們運行結果為true,因為此時它們引用的都是同一個實例~~所以是符合單例模式的

我們可以發現,單例模式的實現,即外部無法創建一個實例是通過將構造方法變為private來實現的,此時的話外部只能通過getInstance()方法來訪問內部已經創建好的那個實例

線程安全問題

那么,餓漢模式有沒有線程安全問題呢?我們可以發現,餓漢模式中只有讀操作,所以是沒有線程安全問題的~~可以放心大膽使用

不過餓漢模式還是有缺點的,因為實例一開始就被創建了,一直等著被使用,這是很浪費資源的行為~~

懶漢模式

什么是懶漢模式

所謂的懶漢模式,指的是實例只有在被使用的時候才會開始創建,因此聽起來就很懶啦,所以就被叫做懶漢模式,不過這里的"懶"可是褒義詞,因為這可以節約資源~~

懶漢模式實現

class SingletonLazy {private static SingletonLazy instance = null;public static SingletonLazy getInstance() {if (instance == null) {             //判斷是否有實例創建了instance = new SingletonLazy();}return instance;}private SingletonLazy() {}
}
public class Demo20 {   //懶漢模式public static void main(String[] args) {SingletonLazy t1 = SingletonLazy.getInstance();SingletonLazy t2 = SingletonLazy.getInstance();System.out.println(t1 == t2);}
}

上述我們給出了一個按照描述的懶漢模式,實例一開始是null,只有當被調用的時候才會根據判斷實例是否創建過來創建實例

線程安全問題

當我們細看上述代碼的時候,其實可以發現,對于instance對象,我們既有讀操作,又有寫操作,所以事實上,這個代碼是存在線程安全問題的

當t1和t2同時調用的時候,有可能會創建兩個實例,這就不符合單例模式的要求了;

我們細看可以發現,此時創建實例這個操作并不是原子的,是if + 創建一起的,這也是會引發線程安全問題的原因

因此,為了解決上述問題,我們可以把這個操作加個鎖,把它們變成原子的~~

解決原子性問題代碼
class SingletonLazy {private static SingletonLazy instance = null;private static Object locker = new Object();public static SingletonLazy getInstance() {synchronized (locker) {                 //使得if和創建實例變成一個整體的原子操作,防止多個線程同時操作時創建多個實例if (instance == null) {             //判斷是否有實例創建了instance = new SingletonLazy();}}return instance;}private SingletonLazy() {}
}
public class Demo20 {   //懶漢模式public static void main(String[] args) {SingletonLazy t1 = SingletonLazy.getInstance();SingletonLazy t2 = SingletonLazy.getInstance();System.out.println(t1 == t2);}
}

OK~~我們的原子性問題已經解決啦(●'?'●)

但是...我們再來看下這個代碼,線程安全問題有沒有解決完呢?回顧我們P4中提到的引起線程安全問題的原因,此時其實我們是兩個線程在對一個對象進行操作,所以是很可能發生指令重排序和內存可見性問題的,因此我們應該給這個對象加上volatile~~

解決指令重排序和內存可見性問題
class SingletonLazy {private static volatile SingletonLazy instance = null;  //volatile防止指令重排序private static Object locker = new Object();public static SingletonLazy getInstance() {synchronized (locker) {                 //使得if和創建實例變成一個整體的原子操作,防止多個線程同時操作時創建多個實例if (instance == null) {             //判斷是否有實例創建了instance = new SingletonLazy();}}return instance;}private SingletonLazy() {}
}
public class Demo20 {   //懶漢模式public static void main(String[] args) {SingletonLazy t1 = SingletonLazy.getInstance();SingletonLazy t2 = SingletonLazy.getInstance();System.out.println(t1 == t2);}
}

OK~~~這下這兩個問題也解決啦(●'?'●)

這樣看起來,線程好像是沒啥安全問題了勒,那有沒有啥可以優化的?

經典生活例子

為了使大家更好理解我們的優化策略,在po出代碼之前,我們先搞個生活化例子理解一下~~

比如說學校里面的校花突然恢復單身了,那廣大單身男青年們聽到這個消息之后都很開心吶,不過大家素質都很高,于是在追求校花的時候排起了隊,按序追求,你很幸運的搶到了第一個,你開始追求校花之后就相當于追求校花這個操作加鎖了,別人就不能進行了,校花覺得你特別好,于是答應和你在一起啦~~[成功創建了實例],但是隊伍里面的人此時還不知道,第二個人此時又去追求校花,追求校花這個操作就又加鎖了,這個時候她就說我有男朋友啦,第二個人出去是不是就會告訴其它人,校花有男朋友了這件事情,那么實際上,他們就不會再進行追求校花這個操作了,即甚至連競爭鎖這個操作都不會去做,因為這是浪費資源的

所以與例子同樣的問題,我們這個代碼此時每個線程都還會再去競爭一次鎖,但如果實例已經存在了,就沒有競爭鎖的必要了,只有一開始的幾個線程在實例被創建好之前就進入創建實例過程會競爭一下鎖~~~

解決重復競爭鎖問題
class SingletonLazy {private static volatile SingletonLazy instance = null;  //volatile防止指令重排序private static Object locker = new Object();public static SingletonLazy getInstance() {if (instance == null) {                     //后續線程可以防止重復加鎖synchronized (locker) {                 //使得if和創建實例變成一個整體的原子操作,防止多個線程同時操作時創建多個實例if (instance == null) {             //判斷是否有實例創建了instance = new SingletonLazy();}}}return instance;}private SingletonLazy() {}
}
public class Demo20 {   //懶漢模式public static void main(String[] args) {SingletonLazy t1 = SingletonLazy.getInstance();SingletonLazy t2 = SingletonLazy.getInstance();System.out.println(t1 == t2);}
}

標準懶漢模式實現代碼

我們這里總結一下解決完所有問題之后的代碼

class SingletonLazy {private static volatile SingletonLazy instance = null;  //volatile防止指令重排序private static Object locker = new Object();public static SingletonLazy getInstance() {if (instance == null) {                     //后續線程可以防止重復加鎖synchronized (locker) {                 //使得if和創建實例變成一個整體的原子操作,防止多個線程同時操作時創建多個實例if (instance == null) {             //判斷是否有實例創建了instance = new SingletonLazy();}}}return instance;}private SingletonLazy() {}
}
public class Demo20 {   //懶漢模式public static void main(String[] args) {SingletonLazy t1 = SingletonLazy.getInstance();SingletonLazy t2 = SingletonLazy.getInstance();System.out.println(t1 == t2);}
}

??感謝觀看~~對你有幫助的話給博主點個大大的贊吧(●'?'●)謝謝~~~??

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

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

相關文章

kubernetes中數據存儲etcd

etcd 在 Kubernetes 中的角色核心定位:Kubernetes 的 唯一持久化數據存儲(一致性數據庫)。職責: 保存整個集群的期望狀態(desired state),包括節點信息、Pod 清單、Service 定義、ConfigMap、Se…

Linux crontab定時任務

參考資料 【図解】cronの仕組み定時任務 - crontab解決ubuntu下定時任務不執行問題crontab環境變量問題💥Linux定時任務功能詳解:crontab與at命令應用指南 目錄一. 環境準備1.1 wsl開啟systemd1.2 開啟cron日志二. cron服務管理相關命令2.1 service 的方…

企業頻繁收到軟件律師函?如何徹底解決這一難題

1. 引言:律師函頻發,已成信息化管理的“隱形雷區”在工業制造、芯片、航空航天、船舶制造、醫療器械等高要求行業,軟件不僅是研發與生產的關鍵工具,更是企業數據與知識產權安全的“底座”。然而,不少企業卻在日常運營中…

在 macOS 上順利安裝 lapsolver

一、什么是 lapsolver? lapsolver 是一個用于求解線性分配問題(Linear Assignment Problem, LAP) 的 Python 庫。線性分配問題是運籌學中的經典問題,核心是在兩個集合(如“工人”與“任務”)之間找到一組最…

宋紅康 JVM 筆記 Day02|JVM的架構模型、生命周期、發展歷程

一、今日視頻區間 P13-P25 二、一句話總結 JVM的架構模型;JVM的生命周期;JVM發展歷程; 三、關鍵圖/命令 3.1 JVM的架構模型Java程序對.class字節碼文件進行反編譯操作:在idea中先運行需要反編譯的代碼,找到對應的字節碼…

Linux新手上路 | 在Ubuntu上Pluma文本編輯器的安裝與基本使用

Linux新手上路 | 在Ubuntu上Pluma文本編輯器的安裝與基本使用一、Pluma工具介紹1.1 Pluma 工具概述1.2 主要功能1.3 適用場景二、安裝Pluma2.1 安裝方法2.2 啟動Pluma工具三、漢化方法3.1 安裝漢化包3.2 設置系統語言3.3 重新打開Pluma四、基本使用方法4.1 編寫文本內容4.2 關鍵…

React 揭秘:從新手到高手的進階之路

目錄 React:前端開發新寵? React 初相識? 什么是 React? React 的核心特性? 1.組件化開發 2.虛擬 DOM 與 Diff 算法 單向數據流 搭建 React 開發環境 環境準備? 創建 React 項目 項目結構解析 React 基礎語法與核心概念 JSX 語法? 基本語法規則…

八股文小記 Servlet 過濾器-Spring MVC 攔截器-Spring AOP 攔截器區別

您對執行機制的洞察非常準確!讓我們深入分析這三種組件的調用機制及其與 AOP 節點的關系: 一、執行機制的本質區別組件調用機制實現原理Servlet 過濾器遞歸調用通過 FilterChain.doFilter() 顯式遞歸調用下一個節點Spring MVC 攔截器遍歷調用由 HandlerE…

qml 實現數值鍵盤

import QtQuick 2.0import QtQuick.Layouts 1.12 import"../pad" // PasswordKeyboard.qml import QtQuick 2.12ColumnLayout {id: keyboardspacing: 8// 鍵盤標題Text {text: "安全輸入"font.pixelSize: 16color: "#666"Layout.alignment: Qt.A…

PID控制算法

文章目錄引言一、基本原理1.1.簡介1.2.開環與閉環1.3.PID 的公式1.3.1.比例項(Proportional)1.3.2.積分項(Integral)1.3.3.微分項(Differential)1.4.連續形式與離散形式的 PID 公式1.4.1.連續形式1.4.2.離散…

MyBatis 動態數據源切換在 Spring Boot 環境下的實現方案

第一章 需求背景與技術選型1.1 多數據源場景概述在大型企業級應用中,單一數據庫往往無法滿足高并發和多業務線的需求,因此需要引入 多數據源 的架構設計。常見的多數據源場景包括:讀寫分離、多租戶、分庫分表以及數據源負載均衡等。讀寫分離&…

PCA降維理論詳解

文章目錄一、什么是PCA?二、為什么需要降維?三、PCA的數學原理與詳細推導視角一:最大化投影方差(Maximizing Variance)視角二:最小化重構誤差(Minimizing Reconstruction Error)四、…

Android RxJava變換操作符詳解

RxJava作為響應式編程在Android開發中的利器,其強大的變換操作符能夠幫助我們優雅地處理數據流。本文將深入講解RxJava中最常用的變換操作符及其實際應用場景。一、RxJava變換操作符概述變換操作符(Transformation Operators)用于對Observable發射的數據序列進行變換…

開源數據發現平臺:Amundsen 快速上手指南

Amundsen 是一個數據發現和元數據引擎,旨在提高數據分析師、數據科學家和工程師與數據交互時的生產力。目前,它通過索引數據資源(表格、儀表板、數據流等)并基于使用模式(例如,查詢頻率高的表格會優先于查詢…

【密碼學實戰】國密SM2算法介紹及加解密/簽名代碼實現示例

引言 在信息安全領域,密碼算法是數據保護的核心基石。2010 年,中國國家密碼管理局發布了 SM2 橢圓曲線公鑰密碼算法,作為國產密碼標準的核心成員,它憑借高效安全的特性,逐步替代 RSA 等國際算法,廣泛應用于…

QT開發中如何加載第三方dll文件

文章目錄🔧 一、隱式加載(靜態鏈接)操作步驟:?? 二、顯式加載(動態鏈接,推薦使用QLibrary)操作步驟:💻 三、直接調用Windows API(僅Windows)??…

后端學習資料 持續更新中

數據庫: 該網址包含:圖解MySql, 看明白誰也問不倒你~ 圖解計算機網絡、操作系統、計算機組成、MySQL、Redis,讓天下沒有難懂的八股文!https://xiaolincoding.com/

《嵌入式Linux應用編程(六):并發編程基礎:多進程exec函數族及多線程基礎》

一、exec函數族在一個進程里面執行另一個文件本質&#xff1a;將文本區的指令代碼替換成exec要執行的指令#include <unistd.h>參數&#xff1a;path:要執行的可執行文件的路徑和名稱arg:執行該可執行文件時需要傳遞的參數NULL&#xff1a;參數傳遞結束標志 返回值&#x…

【121頁PPT】智慧方案智慧綜合體智能化設計方案(附下載方式)

篇幅所限&#xff0c;本文只提供部分資料內容&#xff0c;完整資料請看下面鏈接 https://download.csdn.net/download/2501_92808859/91654007 資料解讀&#xff1a;【121頁PPT】智慧方案智慧綜合體智能化設計方案 詳細資料請看本解讀文章的最后內容 一、項目概述與智能化總…

Linux網絡基礎(一)

目錄 計算機網絡背景 網絡發展 初識 "協議" 網絡協議初識 協議分層 軟件分層的好處 打電話例子 OSI七層模型 TCP/IP五層(或四層)模型 參考資料 再識協議 為什么要有 TCP/IP 協議&#xff1f; 什么是 TCP/IP 協議&#xff1f; TCP/IP 協議與操作系統的關系(宏觀上&…