Java設計模式系列:單例設計模式

Java設計模式系列:單例設計模式

介紹

所謂類的單例設計模式,就是采取一定的方法保證在整個的軟件系統中,對某個類只能存在一個對象實例,并且該類只提供一個取得其對象實例的方法(靜態方法)

比如 Hibernate 的 SessionFactory,它充當數據存儲源的代理,并負責創建 Session 對象。SessionFactory 并不是輕量級的,一般情況下,一個項目通常只需要一個 SessionFactory 就夠,這是就會使用到單例模式

八種方式

  • 1)餓漢式(靜態常量)

  • 2)餓漢式(靜態代碼塊)

  • 3)懶漢式(線程不安全)

  • 4)懶漢式(線程安全,同步方法)

  • 5)懶漢式(線程不安全,同步代碼塊)

  • 6)雙重檢查

  • 7)靜態內部類

  • 8)枚舉

1、餓漢式(靜態常量)

  • 1)構造器私有化(防止外部 new)

  • 2)類的內部創建對象

  • 3)向外暴露一個靜態的公共方法 getInstance

package com.mcode.api.singleton.type1;/*** ClassName: SingletonTest01* Package: com.mcode.api.singleton.type1* Description:** @Author: robin* @Create: 2019/11/22 - 9:41 PM* @Version: v1.0*/
public class SingletonTest01 {public static void main(String[] args) {Singleton instance = Singleton.getInstance();Singleton instance1 = Singleton.getInstance();System.out.println(instance == instance1); //trueSystem.out.println("instance.hashCode=" + instance.hashCode());System.out.println("instance.hashCode=" + instance1.hashCode());}
}//餓漢式(靜態變量)
class Singleton {// 1、構造器私有化private Singleton() {}// 2、類的內部創建對象private static final Singleton instance = new Singleton();// 3、向外暴露一個靜態的公共方法public static Singleton getInstance() {return instance;}
}

優缺點

  • 1)優點:這種寫法比較簡單,就是在類裝載的時候就完成實例化。避免了線程同步問題

  • 2)缺點:在類裝載的時候就完成實例化,沒有達到 Lazy Loading 的效果。如果從始至終從未使用過這個實例,則會造成內存的浪費

  • 3)這種方式基于 classloder 機制避免了多線程的同步問題。不過,instance 在類裝載時就實例化,在單例模式中大多數都是調用getlnstance 方法,但是導致類裝載的原因有很多種,因此不能確定有其他的方式(或者其他的靜態方法)導致類裝載,這時候初始化 instance 就沒有達到 Lazy loading 的效果

  • 4)結論:這種單例模式可用,可能造成內存浪費

2、餓漢式(靜態代碼塊)

  • 1)構造器私有化

  • 2)類的內部聲明對象

  • 3)在靜態代碼塊中創建對象

  • 4)向外暴露一個靜態的公共方法

public class Singleton {// 1、構造器私有化private Singleton() {}// 2、類的內部聲明對象private static Singleton instance;// 3、在靜態代碼塊中創建對象static {instance = new Singleton();}// 4、向外暴露一個靜態的公共方法public static Singleton getInstance() {return instance;}
}

優缺點

  • 1)這種方式和上面的方式其實類似,只不過將類實例化的過程放在了靜態代碼塊中,也是在類裝載的時候,就執行靜態代碼塊中的代碼,初始化類的實例。優缺點和上面是一樣的。

  • 2)結論:這種單例模式可用,但是可能造成內存浪費

3、懶漢式(線程不安全)

  • 1)構造器私有化

  • 2)類的內部創建對象

  • 3)向外暴露一個靜態的公共方法,當使用到該方法時,才去創建 instance

// 1、構造器私有化
private Singleton() {
}// 2、類的內部聲明對象
private static Singleton instance;// 3、向外暴露一個靜態的公共方法,當使用到該方法時,才去創建 instance
public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;
}

優缺點

  • 1)起到了 Lazy Loading 的效果,但是只能在單線程下使用

  • 2)如果在多線程下,一個線程進入了判斷語句塊,還未來得及往下執行,另一個線程也通過了這個判斷語句,這時便會產生多個實例

  • 3)結論:在實際開發中,不要使用這種方式

4、懶漢式(線程安全,同步方法)

  • 1)構造器私有化

  • 2)類的內部創建對象

  • 3)向外暴露一個靜態的公共方法,加入同步處理的代碼,解決線程安全問題

public class Singleton {// 1、構造器私有化private Singleton() {}// 2、類的內部聲明對象private static Singleton instance;// 3、向外暴露一個靜態的公共方法,加入同步處理的代碼,解決線程安全問題public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

優缺點

  • 1)解決了線程不安全問題

  • 2)效率太低了,每個線程在想獲得類的實例時候,執行getlnstance()方法都要進行同步。而其實這個方法只執行一次實例化代碼就夠了,后面的想獲得該類實例,直接return就行了。方法進行同步效率太低

  • 3)結論:在實際開發中,不推薦使用這種方式

5、懶漢式(線程不安全,同步代碼塊)

  • 1)構造器私有化

  • 2)類的內部創建對象

  • 3)向外暴露一個靜態的公共方法,加入同步處理的代碼塊

public class Singleton {// 1、構造器私有化private Singleton() {}// 2、類的內部聲明對象private static Singleton instance;// 3、向外暴露一個靜態的公共方法,加入同步處理的代碼,解決線程安全問題public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {instance = new Singleton();}}return instance;}
}

優缺點

  • 1)這種方式,本意是想對第四種實現方式的改進,因為前面同步方法效率太低,改為同步產生實例化的的代碼塊

  • 2)但是這種同步并不能起到線程同步的作用。跟第3種實現方式遇到的情形一致,假如一個線程進入了判斷語句塊,還未來得及往下執行,另一個線程也通過了這個判斷語句,這時便會產生多個實例

  • 3)結論:在實際開發中,不能使用這種方式

6、雙重檢查

  • 1)構造器私有化

  • 2)類的內部創建對象,同時用volatile關鍵字修飾修飾

  • 3)向外暴露一個靜態的公共方法,加入同步處理的代碼塊,并進行雙重判斷,解決線程安全問題

public class Singleton {// 1、構造器私有化private Singleton() {}// 2、類的內部聲明對象,同時用`volatile`關鍵字修飾修飾private static volatile Singleton instance;// 3、向外暴露一個靜態的公共方法,加入同步處理的代碼塊,并進行雙重判斷,解決線程安全問題public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

優缺點

  • 1)Double-Check 概念是多線程開發中常使用到的,我們進行了兩次檢查,這樣就可以保證線程安全了

  • 2)這樣實例化代碼只用執行一次,后面再次訪問時直接 return 實例化對象,也避免的反復進行方法同步

  • 3)線程安全;延遲加載;效率較高

  • 4)結論:在實際開發中,推薦使用這種單例設計模式

7、靜態內部類

  • 1)構造器私有化

  • 2)定義一個靜態內部類,內部定義當前類的靜態屬性

  • 3)向外暴露一個靜態的公共方法

public class Singleton {// 1、構造器私有化private Singleton() {}// 2、定義一個靜態內部類,內部定義當前類的靜態屬性private static class SingletonInstance {private static final Singleton instance = new Singleton();}// 3、向外暴露一個靜態的公共方法public static Singleton getInstance() {return SingletonInstance.instance;}
}

優缺點

  • 1)這種方式采用了類裝載的機制,來保證初始化實例時只有一個線程

  • 2)靜態內部類方式在 Singleton 類被裝載時并不會立即實例化,而是在需要實例化時,調用getlnstance方法,才會裝載Singletonlnstance 類,從而完成 Singleton 的實例化

  • 3)類的靜態屬性只會在第一次加載類的時候初始化,JVM幫助我們保證了線程的安全性,在類進行初始化時,別的線程是無法進入的

  • 4)優點:避免了線程不安全,利用靜態內部類特點實現延遲加載,效率高

  • 5)結論:推薦使用

8、枚舉

public enum Singleton {INSTANCE;public void sayHello() {System.out.println("Hello World");}
}

優缺點

  • 1)這借助 JDK1.5 中添加的枚舉來實現單例模式。不僅能避免多線程同步問題,而且還能防止反序列化重新創建新的對象

  • 2)這種方式是 Effective Java 作者 Josh Bloch 提倡的方式

  • 3)結論:推薦使用

JDK 源碼分析

JDK中 java.lang.Runtime 就是經典的單例模式

注意事項和細節說明

  • 1)單例模式保證了系統內存中該類只存在一個對象,節省了系統資源,對于一些需要頻繁創建銷毀的對象,使用單例模式可以提高系統性能

  • 2)當想實例化一個單例類的時候,必須要記住使用相應的獲取對象的方法,而不是使用 new

  • 3)單例模式使用的場景:需要頻繁的進行創建和銷毀的對象、創建對象時耗時過多或耗費資源過多但又經常用到的對象(即:重量級對象)、工具類對象、頻繁訪問數據庫或文件的對象(比如數據源、session 工廠等)

雖然上述提到的概念中,將雙重檢查、靜態內部類、枚舉三種方式的單例模式單獨列舉出來說明,但個人覺得本質也可以歸類到餓漢式和懶漢式中;另外,同步代碼塊雖然上述中歸類到線程安全,實際上并不是線程安全的

總結如下

  • |——餓漢式:靜態常量、靜態代碼塊、枚舉(本質就是靜態常量)

  • |——懶漢式

    • |——線程不安全:一次檢查、同步代碼塊
    • |——線程安全:同步方法、雙重檢查、靜態內部類

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

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

相關文章

循環隊列的幾種實現方式

基于數組來實現循環隊列的幾種方式 使用 usedSize使用 標志位空出一個位置 方式一 usedSize 0 是則表示當前數組為空 usedSize 數組的長度 則表示當前數組滿了方式二 使用一個標記 flag 來判斷當前隊列是空還是滿 如果 flag false 則說明隊列為空 如果 flag true 則說…

leetcode:495. 提莫攻擊

一、題目 鏈接&#xff1a;495. 提莫攻擊 - 力扣&#xff08;LeetCode&#xff09; 函數原型&#xff1a;int findPoisonedDuration(int* timeSeries, int timeSeriesSize, int duration) 二、思路 遍歷數組timeSeries&#xff0c;如果 元素值duration < 下一元素值 &#x…

GO語言實現txt文本多行合并為一行

windows系統txt文件 input.txt多行合并為一行 package mainimport ("fmt""io/ioutil""strings" )func main() {content, err : ioutil.ReadFile("E:\\gin_demo\\demo30DNF\\youhua\\input.txt")if err ! nil {fmt.Println("Err…

OpenCV快速入門:圖像分析——傅里葉變換、積分圖像

文章目錄 前言一、傅里葉變換1.1 離散傅里葉變換1.1.1 離散傅里葉變換原理1.1.2 離散傅里葉變換公式1.1.3 代碼實現1.1.4 cv2.dft 函數解析 1.2 傅里葉變換進行卷積1.2.1 傅里葉變換卷積原理1.2.2 傅里葉變換卷積公式1.2.3 代碼實現1.2.4 cv2.mulSpectrums 函數解析 1.3 離散余…

基于深度學習的文本分類

通過構建更復雜的深度學習模型可以提高分類的準確性&#xff0c;即分別基于TextCNN、TextRNN和TextRCNN三種算法實現中文文本分類。 項目地址&#xff1a;zz-zik/NLP-Application-and-Practice: 本項目將《自然語言處理與應用實戰》原書中代碼進行了實現&#xff0c;并在此基礎…

Unity使用DOTween實現分段進度條

文章目錄 需求下載安裝 DOTween實現實現效果 需求 用組件進度條&#xff08;Slider&#xff09;&#xff0c;利用分段加載進行以假亂真的進度效果&#xff0c;比如說2秒鐘到達20%的進度&#xff0c;10秒鐘加載20%到50%進度&#xff0c;1分鐘加載50%到90%的進度&#xff0c;30秒…

2023年金融信創行業研究報告

第一章 行業概況 1.1 定義 金融信創是指在金融行業中應用的信息技術&#xff0c;特別是那些涉及到金融IT基礎設施、基礎軟件、應用軟件和信息安全等方面的技術和產品。這一概念源于更廣泛的“信創 (信息技術應用創新)”&#xff0c;即通過中國國產信息技術替換海外信息技術&a…

77 組合問題

給定兩個整數 n 和 k&#xff0c;返回 1 ... n 中所有可能的 k 個數的組合。 class Solution { private: vector<vector<int>> result; // 存放符合條件結果的集合 vector<int> path; // 用來存放符合條件結果 void backtracking(int n, int k , int st…

測試在 Oracle 下直接 rm dbf 數據文件并重啟數據庫

創建一個新的表空間并創建新的用戶&#xff0c;指定新表空間為新用戶的默認表空間 create tablespace zzw datafile /oradata/cesdb/zzw01.dbf size 10m;zzw用戶已經創建過&#xff0c;這里修改其默認表空間 alter user zzw quota unlimited on zzw; alter user zzw default …

ELK企業級日志分析平臺——logstash

部署 新建一臺虛擬機elk4部署logstash [rootelk4 ~]# yum install -y jdk-11.0.15_linux-x64_bin.rpm[rootelk4 ~]# yum install -y logstash-7.6.1.rpm 命令方式 [rootelk4 bin]# /usr/share/logstash/bin/logstash -e input { stdin { } } output { stdout {} } elasticsearc…

使用骨傳導耳機會傷耳朵嗎?一文讀懂骨傳導耳機有哪些優點

首先說明&#xff0c;如果是正確的使用骨傳導耳機是不會傷耳朵。 一、骨傳導耳機的傳聲原理是什么&#xff1f; 聲音的傳播需要介質&#xff0c;傳統的耳機是通過空氣來進行傳播&#xff0c;也被稱為“空氣傳導耳機”&#xff0c;而骨傳導耳機最大的特別之處就在于&#xff0…

AcWing 3384:二叉樹遍歷(依先序序列建樹,輸出中序序列) ← DFS

【題目來源】https://www.acwing.com/problem/content/3387/【題目描述】 編寫一個程序&#xff0c;讀入用戶輸入的一串先序遍歷字符串&#xff0c;根據此字符串建立一個二叉樹&#xff08;以指針方式存儲&#xff09;。 例如如下的先序遍歷字符串&#xff1a;abc##de#g##f###&…

錄像機IP地址設置教程:輕松掌握網絡連接方法

隨著科技的發展&#xff0c;現在的錄像機都具備了網絡連接的功能&#xff0c;可以通過設置IP地址實現遠程和監控。但是很多人對于錄像機IP地址的設置方法感到困惑。虎觀代理小二二將在本文詳細介紹錄像機IP地址的設置步驟&#xff0c;幫助您輕松掌握網絡連接方法。 首先&#x…

DFS序和歐拉序的降維打擊

1. DFS 序和時間戳 1.1 DFS 序 定義&#xff1a;樹的每一個節點在深度優先遍歷中進、出棧的時間序列。 如下樹的 dfs 序就是[1,2,8,8,5,5,2,4,3,9,9,3,6,6,4,7,7,1]。 下圖為生成DFS的過程。對于一棵樹進行DFS序&#xff0c;除了進入當前節點時對此節點進行記錄&#xff0c;…

多線程Thread(初階二:Thread類及常??法)

目錄 一、Thread 的常?構造?法 繼承Thread代碼&#xff1a; 實現Runnable接口代碼: 二、Thread 的?個常?屬性 1、id&#xff1a; 2、獲取線程的名字。 3、進程的狀態&#xff1a; 4、在java中設置的優先級&#xff0c; 5、是否后臺線程&#xff0c; 6、是否存活&a…

ubuntu22.04 arrch64版在線安裝node

腳本 #安裝node#下載node、npm國內鏡像&#xff08;推薦&#xff09;# 判斷是否安裝了nodeif type -p node; thenecho "node has been installed."elsemkdir -p /home/zenglg cd /home/zenglgwget https://registry.npmmirror.com/-/binary/node/v10.14.1/node-v10.…

Linux系統編程 day04 文件和目錄操作

Linux系統編程 day04 文件和目錄操作 1. 文件IO1.1 open 函數1.2 close函數1.3 read函數1.4 write函數1.5 lseek函數1.6 errno變量1.7 文件示例1 讀寫文件1.8 文件示例2 文件大小的計算1.9 文件示例3 擴展文件大小1.10 文件示例4 perror函數的使用1.11 阻塞與非阻塞的測試 2. 文…

關于「光學神經網絡」的一切:理論、應用與發展

/目錄/ 一、線性運算的光學實現 1.1. 光學矩陣乘法器 1.2. 光的衍射實現線性運行 1.3. 基于Rayleigh-Sommerfeld方程的實現方法 1.4. 基于傅立葉變換的實現 1.5. 通過光干涉實現線性操作 1.6. 光的散射實現線性運行 1.7. 波分復用&#xff08;WDM&#xff09;實現線性運…

Educoder中MATLAB數值計算與符號計算

第1關&#xff1a;數據處理 a[20 5 7 19 23 14 25 67 23 12]; %%%%%%%%% Begin %%%%%%%% smaxmax(a); sminmin(a); smeanmean(a); smedianmedian(a); ssumsum(a); %%%%%%%%% End %%%%%%%%% m[smax;smin;smean;smedian;ssum]; disp(m); 第2關&#xff1a;多項式計算與數值微積…

脈沖幅度調制信號的功率譜計算

本篇文章是博主在通信等領域學習時&#xff0c;用于個人學習、研究或者欣賞使用&#xff0c;并基于博主對人工智能等領域的一些理解而記錄的學習摘錄和筆記&#xff0c;若有不當和侵權之處&#xff0c;指出后將會立即改正&#xff0c;還望諒解。文章分類在通信領域筆記&#xf…