淺析Java單例設(shè)計(jì)模式(自寫demo)
單例模式特點(diǎn)
1、構(gòu)造器私有
2、在一個(gè)Java應(yīng)用程序中,可保證只有一個(gè)實(shí)例對(duì)象
3、只提供一個(gè)供外界調(diào)用的getInstance()方法
單例模式優(yōu)點(diǎn)
1、減少某些對(duì)象的頻繁創(chuàng)建,降低系統(tǒng)開銷和內(nèi)存占用
2、外部調(diào)用不使用new關(guān)鍵字,降低系統(tǒng)內(nèi)存的使用頻率
3、對(duì)于特殊的類,在系統(tǒng)中只能存在一個(gè)實(shí)例,否則系統(tǒng)無法正常運(yùn)行,比如Controller
實(shí)現(xiàn)方式
這里簡(jiǎn)單介紹兩種實(shí)現(xiàn)方式
餓漢式(線程安全)
/**
* @author: xuzhilei6656
* @create: 2021-12-12 12:07
* @description: 單例模式(餓漢式)
**/
public class Singleton {
//創(chuàng)建實(shí)例
private static Singleton instance = new Singleton();
//私有構(gòu)造器
private Singleton(){
}
//獲取實(shí)例的靜態(tài)方法
public static Singleton getInstance(){
return instance;
}
}
實(shí)例對(duì)象在類被加載的時(shí)候就已經(jīng)完成初始化,外界調(diào)用拿到的都是這個(gè)唯一的實(shí)例對(duì)象
懶漢式
/**
* @author: xuzhilei6656
* @create: 2021-12-12 12:22
* @description: 單例模式(懶漢式)
**/
public class Singleton {
//聲明一個(gè)變量
private static Singleton instance;
//私有構(gòu)造器
private Singleton(){
}
//獲取實(shí)例的靜態(tài)方法
public static Singleton getInstance(){
//如果是首次調(diào)用,實(shí)例對(duì)象還沒有被創(chuàng)建,就需要?jiǎng)?chuàng)建,否則都是返回已經(jīng)創(chuàng)建過的那個(gè)對(duì)象
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
對(duì)比餓漢式可見,實(shí)例對(duì)象在類被加載的時(shí)候并沒有進(jìn)行創(chuàng)建,在首次調(diào)用的時(shí)候才被創(chuàng)建,以后再被調(diào)用,返回的也是那個(gè)唯一的實(shí)例對(duì)象。
在多線程情況下,這種寫法存在線程安全問題,比如:線程A在執(zhí)行完if判斷條件后進(jìn)入阻塞狀態(tài),此時(shí)并沒有進(jìn)行對(duì)象創(chuàng)建,此時(shí)線程B來了,在執(zhí)行完if條件后直接進(jìn)行對(duì)象創(chuàng)建,等線程A恢復(fù)運(yùn)行狀態(tài)后也會(huì)進(jìn)行對(duì)象創(chuàng)建,這個(gè)時(shí)候就不符合單例模式了,即出現(xiàn)了線程不安全的問題。
解決方案:在獲取實(shí)例的靜態(tài)方法上加synchronized關(guān)鍵字,即加鎖
/**
* @author: xuzhilei6656
* @create: 2021-12-12 12:22
* @description: 單例模式(懶漢式)
**/
public class Singleton {
//聲明一個(gè)變量
private static Singleton instance;
//私有構(gòu)造器
private Singleton(){
}
//獲取實(shí)例的靜態(tài)方法
public static synchronized Singleton getInstance(){
//如果是首次調(diào)用,實(shí)例對(duì)象還沒有被創(chuàng)建,就需要?jiǎng)?chuàng)建,否則都是返回已經(jīng)創(chuàng)建過的那個(gè)對(duì)象
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
簡(jiǎn)單粗暴,可達(dá)到我們的目的,但是每次獲取實(shí)例對(duì)象都要有加鎖操作,影響系統(tǒng)性能。
改進(jìn)后的方案:雙重檢查
/**
* @author: xuzhilei6656
* @create: 2021-12-12 12:22
* @description: 單例模式(懶漢式)
**/
public class Singleton {
//聲明一個(gè)變量
private static Singleton instance;
//私有構(gòu)造器
private Singleton(){
}
//獲取實(shí)例的靜態(tài)方法
public static synchronized Singleton getInstance(){
//第一次檢查
if (instance == null){
//獲取鎖
synchronized (Singleton.class){
//第二次檢查
if (instance==null){
//兩次檢查都確定沒有已存在的實(shí)例對(duì)象,這才進(jìn)行對(duì)象的創(chuàng)建操作
instance = new Singleton();
}
}
}
return instance;
}
}
這樣不必每次獲取實(shí)例對(duì)象的時(shí)候都進(jìn)行加鎖操作,只有在第一次創(chuàng)建對(duì)象的時(shí)候才進(jìn)行加鎖操作,提高了系統(tǒng)性能。
但是,即使這樣有可能會(huì)出現(xiàn)。因?yàn)?instance = new Singleton()這行代碼在JVM中是兩個(gè)操作,賦值和初始化實(shí)例,但JVM并不保證這兩個(gè)操作的順序,有可能JVM給新對(duì)象分配了空間,直接賦值給instance變量,然后才去做初始化實(shí)例操作。比如下面這種情況
1,A,B兩個(gè)線程都進(jìn)入第一個(gè)if條件
2,A線程先搶到鎖進(jìn)入到synchronized代碼塊,執(zhí)行了instance = new Singleton()這行代碼,然后釋放鎖,此時(shí)有可能JVM只給實(shí)例對(duì)象分配了空白的內(nèi)存空間,并沒有執(zhí)行初始化操作
3,B線程搶到鎖,進(jìn)入到synchronized代碼塊,第二次判斷的時(shí)候發(fā)現(xiàn)instance不是null,直接返回使用卻發(fā)現(xiàn)得到的對(duì)象還沒有被初始化,于是出現(xiàn)了問題。
再次改進(jìn):使用volatile關(guān)鍵字修飾聲明的成員變量instance
/**
* @author: xuzhilei6656
* @create: 2021-12-12 12:22
* @description: 單例模式(懶漢式)
**/
public class Singleton {
//聲明變量,被volatile修飾
private volatile static Singleton instance;
//私有構(gòu)造器
private Singleton(){
}
//獲取實(shí)例的靜態(tài)方法
public static synchronized Singleton getInstance(){
//第一次檢查
if (instance == null){
//獲取鎖
synchronized (Singleton.class){
//第二次檢查
if (instance==null){
//兩次檢查都確定沒有已存在的實(shí)例對(duì)象,這才進(jìn)行對(duì)象的創(chuàng)建操作
instance = new Singleton();
}
}
}
return instance;
}
}
volatile關(guān)鍵字作用:通過volatile修飾的變量,不會(huì)被線程本地緩存,所有線程對(duì)該對(duì)象的讀寫都會(huì)第一時(shí)間同步到主內(nèi)存,從而保證多個(gè)線程間該對(duì)象的準(zhǔn)確性。
這個(gè)寫法已經(jīng)比較完美了,既能保證安全的創(chuàng)建出唯一實(shí)例,又不會(huì)對(duì)系統(tǒng)性能有太大影響。
不過,還有更優(yōu)的寫法:靜態(tài)內(nèi)部類實(shí)現(xiàn)
?
/**
* @author: xuzhilei6656
* @create: 2021-12-12 15:17
* @description: 單例模式
**/
public class Singleton {
//私有構(gòu)造器
private Singleton() {
}
//靜態(tài)內(nèi)部類聲明實(shí)例
private static class SingletonFactory{
private static Singleton instance = new Singleton();
}
//獲取實(shí)例的靜態(tài)方法
public static Singleton getInstance(){
return SingletonFactory.instance;
}
}
?
使用內(nèi)部類來維護(hù)單例的實(shí)現(xiàn),JVM內(nèi)部的機(jī)制能夠保證當(dāng)一個(gè)類被加載的時(shí)候,這個(gè)類的加載過程是線程互斥的。這樣當(dāng)我們第一次調(diào)用getInstance()方法的時(shí)候,JVM能夠保證創(chuàng)建出唯一的實(shí)例對(duì)象,并且這個(gè)實(shí)例對(duì)象是已經(jīng)被初始化完成的,就解決了上面的線程安全問題
最后一種實(shí)現(xiàn)單例的寫法也很完美,代碼最簡(jiǎn)潔
通過枚舉
/**
* @author: xuzhilei6656
* @create: 2021-12-12 15:33
* @description: 單例模式
**/
public enum Singleton {
//代表一個(gè)Singleton實(shí)例
INSTANCE;
}
通過枚舉來實(shí)現(xiàn)單實(shí)例代碼更加簡(jiǎn)潔,而且JVM從根本上保證實(shí)例對(duì)象的唯一性,是更簡(jiǎn)潔、高效、安全的實(shí)現(xiàn)單例的方式?
以上就是淺析Java單例設(shè)計(jì)模式(自寫demo)的詳細(xì)內(nèi)容,更多關(guān)于Java單例模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot 整合 Shiro 密碼登錄與郵件驗(yàn)證碼登錄功能(多 Realm 認(rèn)證)
這篇文章主要介紹了SpringBoot 整合 Shiro 密碼登錄與郵件驗(yàn)證碼登錄(多 Realm 認(rèn)證),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02
Spring Cloud 請(qǐng)求重試機(jī)制核心代碼分析
這篇文章主要介紹了Spring Cloud 請(qǐng)求重試機(jī)制核心代碼分析,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06
mybatis-plus的SafetyEncryptProcessor安全加密處理示例解析
這篇文章主要為大家介紹了mybatis-plus的SafetyEncryptProcessor安全加密處理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
MyBatis_Generator插件的安裝以及簡(jiǎn)單使用方法(圖解)
下面小編就為大家?guī)硪黄狹yBatis_Generator插件的安裝以及簡(jiǎn)單使用方法(圖解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05

