JAVA - 單例設計模式

JAVA - 單例設計模式

    • 一. 簡介
    • 二. 單例模式的原則
    • 三. 單例模式的實現
      • 1.1 餓漢式
        • 1.1.1 靜態變量初始化方式
        • 1.1.2 靜態代碼塊初始化方式
        • 1.1.3 枚舉方式
      • 1.2 懶漢式
        • 1.2.1 懶加載初始化方法 (線程不安全)
        • 1.2.2 懶加載初始化方法 (線程安全)
        • 1.2.3 雙重檢查鎖
        • 1.2.4 靜態內部類方式

前言

這是我在這個網站整理的筆記,有錯誤的地方請指出,關注我,接下來還會持續更新。 作者:神的孩子都在歌唱

一. 簡介

單例模式(Singleton Pattern的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。

這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。

二. 單例模式的原則

  • 單例模式限制類的實例化,并確保Java虛擬機中只存在該類的一個實例。
  • 單例類必須提供一個全局訪問點來獲取該類的實例。
  • 單例模式用于日志記錄、驅動對象、緩存和線程池
  • 單例設計模式還用于其他設計模式,如抽象工廠等。
  • 單例設計模式也用在核心 Java 類中(例如,java.lang.Runtimejava.awt.Desktop)。

三. 單例模式的實現

單例設計模式分類兩種:

? 餓漢式:類加載就會導致該單實例對象被創建

? 懶漢式:類加載不會導致該單實例對象被創建,而是首次使用該對象時才會創建

? 簡單理解:在自己身體里面創建自己,給外部調用

1.1 餓漢式

1.1.1 靜態變量初始化方式

在系統初始化時候,單例類的實例是在類加載時創建的。靜態變量方式初始化的缺點是,即使客戶端應用程序可能沒有使用該方法,也會創建該方法。這是靜態初始化單例類的實現:

/*** @author: 那就叫小智吧* @date: 2022/3/3 15:41* @Description:  靜態變量創建類對象* 	該方式在成員位置聲明Singleton類型的靜態變量,并創建Singleton類的對象instance。instance對象是隨著類的加載而創建的。如果該對象足夠大的話,而一直沒有使用就會造成內存的浪費。*/
public class Singleton1 {// 私有構造方法private Singleton1(){System.out.println("通過靜態變量創建類對象");}// 在成員位置創建該類的對象private static Singleton1 instance= new Singleton1();// 對外提供靜態方法獲取該對象public static Singleton1 getInstance(){return instance;}
}

如果您的單例類沒有使用大量資源,則可以使用這種方法。但在大多數情況下,單例類是為文件系統數據庫連接等資源創建的。除非客戶端調用該getInstance方法,否則我們應該避免實例化。此外,此方法不提供任何異常處理選項。

1.1.2 靜態代碼塊初始化方式

靜態代碼塊方式實現與靜態變量初始化方式類似,不同之處在于類的實例是在提供異常處理選項的靜態塊中創建的。

/*** @author: 那就叫小智吧* @date: 2022/3/3 15:49* @Description: 在靜態代碼塊中創建該類對象* 該方式在成員位置聲明Singleton類型的靜態變量,而對象的創建是在靜態代碼塊中,也是對著類的加載而創建。當然該方式也存在內存浪費問題。*/
public class Singleton2 {// 私有構造方法private Singleton2(){System.out.println("在靜態代碼塊中創建該類對象");};// 在成員位置聲明靜態變量private static Singleton2 instance;static {try {instance = new Singleton2();} catch (Exception e) {throw new RuntimeException("創建單例實例時發生異常");}}// 對外提供靜態方法獲取該對象public static Singleton2 getInstance(){return instance;}}

靜態代碼塊方式實現與靜態變量初始化方式 都會在使用實例之前創建實例,但這不是最佳實踐。

1.1.3 枚舉方式
/*** @author: 那就叫小智吧* @date: 2022/3/3 16:36* @Description: 惡漢式* 枚舉類實現單例模式是極力推薦的單例實現模式,因為枚舉類型是線程安全的,并且只會裝載一次,設計者充分的利用了枚舉的這個特性來實現單例模式,枚舉的寫法非常簡單,而且枚舉類型是所用單例實現中唯一一種不會被破壞的單例實現模式。*/
public enum Singleton {INSTANCE;
}

使用enumJava 來實現單例設計模式,以確保任何enum值在 Java 程序中僅實例化一次。由于Java 枚舉值是全局可訪問的,因此單例也是如此。缺點是enum類型有些不靈活(例如,它不允許延遲初始化)。

1.2 懶漢式

1.2.1 懶加載初始化方法 (線程不安全)
/*** @author: 那就叫小智吧* @date: 2022/3/3 15:59* @Description: 懶漢式 :線程不安全* 從下面面代碼我們可以看出該方式在成員位置聲明Singleton類型的靜態變量,并沒有進行對象的賦值操作,* 那么什么時候賦值的呢?當調用getInstance()方法獲取Singleton類的對象的時候才創建Singleton類的對象,這樣就實現了懶加載的效果。但是,如果是多線程環境,會出現線程安全問題。*/
public class Singleton1 {// 構造私有方法private Singleton1(){System.out.println("懶漢式:線程不安全");}// 在成員位置聲明靜態變量private static Singleton1 instance;// 對外提供靜態方法獲取改對象public static Singleton1 getInstance(){if (instance == null){instance = new Singleton1();}return  instance;}
}

這種實現在單線程環境中工作得很好,但是當涉及到多線程系統時,如果多個線程if同時處于該條件內,則可能會導致問題。它將破壞單例模式,并且兩個線程將獲得單例類的不同實例。

1.2.2 懶加載初始化方法 (線程安全)

創建線程安全單例類的一種簡單方法是使全局訪問方法同步,以便一次只有一個線程可以執行該方法。以下是此方法的一般實現:

/*** @author: 那就叫小智吧* @date: 2022/3/3 16:04* @Description: 懶漢式 : 線程安全* 	該方式也實現了懶加載效果,同時又解決了線程安全問題。但是在getInstance()方法上添加了synchronized關鍵字,導致該方法的執行效果特別低。從上面代碼我們可以看出,其實就是在初始化instance的時候才會出現線程安全問題,一旦初始化完成就不存在了。*/
public class Singleton2 {// 私有構造方法private Singleton2(){};// 在成員位置聲明靜態變量private static Singleton2 instance;// 對外提供靜態方法獲取對象public static synchronized Singleton2 getInstance(){if (instance != null) {instance = new Singleton2();}return  instance;}
}
1.2.3 雙重檢查鎖

前面的實現,能夠正常運行并且提供了線程安全性,但是由于與同步方法相關的成本,它降低了性能,盡管我們只需要它用于可能創建單獨實例的前幾個線程。為了避免每次都產生額外的開銷,使用了雙重檢查鎖定原則

/*** @author: 那就叫小智吧* @date: 2022/3/3 16:08* @Description: 懶漢式 :雙重檢查方式* 對于 `getInstance()` 方法來說,絕大部分的操作都是讀操作,讀操作是線程安全的,所以我們沒必讓每個線程必須持有鎖才能調用該方法,我們需要調整加鎖的時機。由此也產生了一種新的實現模式:雙重檢查鎖模式*/
public class Singleton3 {// 私有構造方法private Singleton3() {System.out.println("懶漢式 : 雙重檢查方式");}// 在成員位置聲明靜態變量private static Singleton3 instance;// 對外提供靜態方法獲取該對象public static Singleton3 getInstance() {// 第一次判斷,如果instance不為null,不進入槍鎖階段,直接返回實例if (instance == null) {synchronized (Singleton3.class) {// 搶到鎖之后再次判斷是否為nullif (instance == null) {instance = new Singleton3();}}}return instance;}
}

檢查鎖模式帶來空指針異常的問題

/*** @author: 那就叫小智吧* @date: 2022/3/3 16:25* @Description: 懶漢式: 雙重檢查方式* 雙重檢查鎖模式是一種非常好的單例實現模式,解決了單例、性能、線程安全問題,上面的雙重檢測鎖模式看上去完美無缺,其實是存在問題,在多線程的情況下,可能會出現空指針問題,出現問題的原因是JVM在實例化對象的時候會進行優化和指令重排序操作。** 要解決雙重檢查鎖模式帶來空指針異常的問題,只需要使用 `volatile` 關鍵字, `volatile` 關鍵字可以保證可見性和有序性。*/
public class Singleton4 {// 私有構造方法private Singleton4() {}private static volatile Singleton4 instance;// 對外提供靜態方法獲取該對象public static Singleton4 getInstance() {//第一次判斷,如果instance不為null,不進入搶鎖階段,直接返回實際if(instance == null) {synchronized (Singleton4.class) {//搶到鎖之后再次判斷是否為空if(instance == null) {instance = new Singleton4();}}}return instance;}}

添加 volatile 關鍵字之后的雙重檢查鎖模式是一種比較好的單例實現模式,能夠保證在多線程的情況下線程安全也不會有性能問題。

1.2.4 靜態內部類方式
/*** @author: 那就叫小智吧* @date: 2022/3/3 16:29* @Description: 懶漢式 : 靜態內部類方式* 靜態內部類單例模式中實例由內部類創建,由于 JVM 在加載外部類的過程中, 是不會加載靜態內部類的, 只有內部類的屬性/方法被調用時才會被加載,并初始化其靜態屬性。靜態屬性由于被 `static` 修飾,保證只被實例化一次,并且嚴格保證實例化順序。*/
public class Singleton5 {// 私有構造方法private Singleton5() {}// 創建靜態內部類private static class SingletonHolder {private static final Singleton5 instance = new Singleton5();}//對外提供靜態方法獲取該對象public static Singleton5 getInstance() {return SingletonHolder.instance;}}
/**
第一次加載Singleton類時不會去初始化INSTANCE,只有第一次調用getInstance,虛擬機加載SingletonHolder并初始化INSTANCE,這樣不僅能確保線程安全,也能保證 Singleton 類的唯一性。*/

文章參考地址

作者:神的孩子都在歌唱
本人博客:https://blog.csdn.net/weixin_46654114
轉載說明:務必注明來源,附帶本人博客連接。

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

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

相關文章

http請求報文

關于urlURL 是大型標識符 URI(統一資源標識)的一部分。這個超集是建立在已有的命名慣例基礎上的。一個 URL 是一個簡單的 URI,使用已存在的協議或規劃(也就是 http,ftp 等)作為地址的一部分。為了進一步描繪這些,我們將會Edit By Vheavens Edit By Vheavens 引入 non-URL 的 UR…

operanbsp;內容攔截

結束我大opera的一個好用功能 內容攔截 博客的主要很清爽,也可以自定義.非常好, 但打開博文,左邊的特別的精彩圖文,瞄到就不高興.好的 開始 右鍵-內容攔截 shift 點那個精彩xx,最右上角,廣告, 然后點完成.ok了 貌似我大遨游的廣告攔截更好用 轉載于:https://www.cnblogs.com/wa…

Solr-5.3.1安裝配置

Solr-5.3.1安裝配置官方網站:http://lucene.apache.org/solr/http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.htmlhttp://lucene.apache.org/solr/quickstart.html環境:CentOS6 x64Solr-5.3.1從Solr5.0開始,Solr不再發布為在任何…

EF for Firebird

今天用了Firebird,記錄下怎么用,不然下次給忘記了 1.官網下載包 1.DDEXProvider-3.0.1.0.msi 2.FirebirdSql.Data.FirebirdClient-4.6.2.0.msi 備注: 1.發現DDEXProvider 與 FirebirdSql.Data.FirebirdClient安裝有先后順序 1.如果中間出現了…

php幾個函數

1. dirname (PHP 3, PHP 4, PHP 5) dirname -- 返回路徑中的目錄部分說明 string dirname ( string path )給出一個包含有指向一個文件的全路徑的字符串,本函數返回去掉文件名后的目錄名。 在 Windows 中,斜線(/)和反斜線&#xf…

Struts2 自定義攔截器(easy example)

要自定義攔截器需要實現com.opensymphony.xwork2.interceptor.Interceptor接口: 新建一個MyInterceptpackage com.action;import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor;public class MyInterceptor i…

img標簽在div里上下居中

方法一&#xff1a;圖片尺寸未知&#xff0c;IE8-不支持 CSS部分&#xff1a; <style> .content{width:500px;height:500px;border:1px solid black;position:relative; } </style>DOM部分&#xff1a; <div class"content"> <img src"htt…

(接口)銀聯證書上傳被修改的問題和讀取證書的絕對路徑問題

最近在做對接銀聯的支付接口。相對于支付寶、微信支付&#xff0c;銀聯支付接口應該是做的最完美的了。銀聯支付平臺提供了足夠詳細的接口文檔&#xff0c;接口demo&#xff0c;常見問題解決方案。 更人性化的是&#xff0c;個人可以免費注冊一個商家賬號&#xff0c;提供給你免…

控制器對視圖聲明周期的控制

init&#xff0d;初始化程序 viewDidLoad&#xff0d;加載視圖 viewWillAppear&#xff0d;UIViewController對象的視圖即將加入窗口時調用&#xff1b; viewDidApper&#xff0d;UIViewController對象的視圖已經加入到窗口時調用&#xff1b; viewWillDisappear&#xff0d;UI…

MST:Bad Cowtractors(POJ 2377)

壞的牛圈建筑 題目大意&#xff1a;就是現在農夫又要牛修建牛欄了&#xff0c;但是農夫想不給錢&#xff0c;于是牛就想設計一個最大的花費的牛圈給他&#xff0c;牛圈的修理費用主要是用在連接牛圈上 這一題很簡單了&#xff0c;就是找最大生成樹&#xff0c;把Kruskal算法改一…

有關進程的一些基本概念

對進程的初步描述 一.和進程有關的一些概念 ①一個進程就是一個正在執行程序的實例&#xff0c;包括程序計數器&#xff0c;寄存器和變量的當前值。 從概念上說&#xff0c;每個進程擁有它自己的虛擬CPU&#xff0c;當然真實的CPU在各個進程之間來回切換。 ②在某一瞬間&…

第4章 maven依賴特性

第4章 maven依賴特性 本章詳細介紹maven的依賴特性和依賴范圍&#xff0c;如何排除依賴。 4.1 什么是依賴傳遞 舉個例子 在非maven項目中&#xff0c;你需要使用spring功能&#xff0c;你會想到導入spring的jar包&#xff0c;那么&#xff0c;srping又需要其他依賴jar包支持&a…

lintcode :Integer to Roman 整數轉羅馬數字

題目 整數轉羅馬數字 給定一個整數&#xff0c;將其轉換成羅馬數字。 返回的結果要求在1-3999的范圍內。 樣例 4 -> IV 12 -> XII 21 -> XXI 99 -> XCIX 更多案例&#xff0c;請戳 http://literacy.kent.edu/Minigrants/Cinci/romanchart.htm 說明 什么是 羅馬數字…

Win32ASm學習[1]:RadASm下測試Debug

okay 正文開始下面的代碼如果不能編譯 請把你的RadAsm下的Masm32這個文件夾復制到任意一個磁盤的根目錄下&#xff0c;在進行編譯就可以了 或者安裝Masm32 SDK到任意磁盤根目錄下 .386.model flat,stdcalloption casemap:none;>>>>>>>>>>&g…

關于javascript閉包

1.閉包的概念 閉包就是能夠讀取其他函數內部變量的函數。 由于在Javascript語言中&#xff0c;只有函數內部的子函數才能讀取局部變量&#xff0c;因此可以把閉包簡單理解成“定義在一個函數內部的函數”。 所以&#xff0c;在本質上&#xff0c;閉包就是將函數內部和函數外部連…

Win32ASM學習[2]:運算符

算術運算符 名稱 優先級 () 圓括號 1 ,- 正、負 2 *,/ 乘、除 3 MOD 取模 3 ,- 加、減 4 ------------------------------------------------------------------------------------------------------------------------------------------ .386 .mo…

正式入住了

從13年開始從事iOS開發工作,就準備寫一些東西,記錄這一路學習工作之旅,但是總是想著堅持不下來,也就慢慢的放棄了,開始用一些記筆記的軟件,印象筆記用過,個人體驗比較差,后來又用了OneNote,這個APP還是比較舒服,但是由于公司的老版mac-pro,無法使用,也就突然想起了,也是時候重出…

VRRP協議具體解釋

轉帖&#xff1a;http://blog.chinaunix.net/space.php?uid11654074&doblog&id2857384 Contents Page 文件夾 入木三分學網絡…

Win32ASM學習[3]:局部變量

.386 .modelflat, stdcall include windows.inc include kernel32.inc include masm32.inc include debug.inc includelib kernel32.lib includelib masm32.lib includelib debug.lib .code main proc ;局部變量中的類型不能使用縮寫 LOCAL v1: dword …