java 單例模式的實例詳解
java 單例模式的實例詳解
概念:
java中單例模式是一種常見的設(shè)計模式,單例模式分三種:懶漢式單例、餓漢式單例、登記式單例三種。
單例模式有一下特點:
1、單例類只能有一個實例。
2、單例類必須自己自己創(chuàng)建自己的唯一實例。
3、單例類必須給所有其他對象提供這一實例。
單例模式確保某個類只有一個實例,而且自行實例化并向整個系統(tǒng)提供這個實例。在計算機(jī)系統(tǒng)中,線程池、緩存、日志對象、對話框、打印機(jī)、顯卡的驅(qū)動程序?qū)ο蟪1辉O(shè)計成單例。這些應(yīng)用都或多或少具有資源管理器的功能。每臺計算機(jī)可以有若干個打印機(jī),但只能有一個Printer Spooler,以避免兩個打印作業(yè)同時輸出到打印機(jī)中。每臺計算機(jī)可以有若干通信端口,系統(tǒng)應(yīng)當(dāng)集中管理這些通信端口,以避免一個通信端口同時被兩個請求同時調(diào)用??傊?,選擇單例模式就是為了避免不一致狀態(tài),避免政出多頭。
首先看一個經(jīng)典的單例實現(xiàn)。
public class Singleton { private static Singleton uniqueInstance = null; private Singleton() { // Exists only to defeat instantiation. } public static Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } // Other methods... }
Singleton通過將構(gòu)造方法限定為private避免了類在外部被實例化,在同一個虛擬機(jī)范圍內(nèi),Singleton的唯一實例只能通過getInstance()方法訪問。(事實上,通過Java反射機(jī)制是能夠?qū)嵗瘶?gòu)造方法為private的類的,那基本上會使所有的Java單例實現(xiàn)失效。此問題在此處不做討論,姑且掩耳盜鈴地認(rèn)為反射機(jī)制不存在。)
但是以上實現(xiàn)沒有考慮線程安全問題。所謂線程安全是指:如果你的代碼所在的進(jìn)程中有多個線程在同時運(yùn)行,而這些線程可能會同時運(yùn)行這段代碼。如果每次運(yùn)行結(jié)果和單線程運(yùn)行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期的是一樣的,就是線程安全的。或者說:一個類或者程序所提供的接口對于線程來說是原子操作或者多個線程之間的切換不會導(dǎo)致該接口的執(zhí)行結(jié)果存在二義性,也就是說我們不用考慮同步的問題。顯然以上實現(xiàn)并不滿足線程安全的要求,在并發(fā)環(huán)境下很可能出現(xiàn)多個Singleton實例。
public class TestStream { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } //該類只能有一個實例 private TestStream(){} //私有無參構(gòu)造方法 //該類必須自行創(chuàng)建 //有2種方式 /*private static final TestStream ts=new TestStream();*/ private static TestStream ts1=null; //這個類必須自動向整個系統(tǒng)提供這個實例對象 public static TestStream getTest(){ if(ts1==null){ ts1=new TestStream(); } return ts1; } public void getInfo(){ System.out.println("output message "+name); } } /** * */ public class TestMain { public static void main(String [] args){ TestStream s=TestStream.getTest(); s.setName("張孝祥"); System.out.println(s.getName()); TestStream s1=TestStream.getTest(); s1.setName("張孝祥"); System.out.println(s1.getName()); s.getInfo(); s1.getInfo(); if(s==s1){ System.out.println("創(chuàng)建的是同一個實例"); }else if(s!=s1){ System.out.println("創(chuàng)建的不是同一個實例"); }else{ System.out.println("application error"); } } }
運(yùn)行結(jié)果:
張孝祥 張孝祥 output message 張孝祥 output message 張孝祥 創(chuàng)建的是同一個實例
結(jié)論:由結(jié)果可以得知單例模式為一個面向?qū)ο蟮膽?yīng)用程序提供了對象惟一的訪問點,不管它實現(xiàn)何種功能,整個應(yīng)用程序都會同享一個實例對象。
1.餓漢式單例類
//餓漢式單例類.在類初始化時,已經(jīng)自行實例化 public class Singleton1 { //私有的默認(rèn)構(gòu)造子 private Singleton1() {} //已經(jīng)自行實例化 private static final Singleton1 single = new Singleton1(); //靜態(tài)工廠方法 public static Singleton1 getInstance() { return single; } }
2.懶漢式單例類
//懶漢式單例類.在第一次調(diào)用的時候?qū)嵗? public class Singleton2 { //私有的默認(rèn)構(gòu)造子 private Singleton2() {} //注意,這里沒有final private static Singleton2 single=null; //靜態(tài)工廠方法 public synchronized static Singleton2 getInstance() { if (single == null) { single = new Singleton2(); } return single; } }
3.登記式單例類
import java.util.HashMap; import java.util.Map; //登記式單例類. //類似Spring里面的方法,將類名注冊,下次從里面直接獲取。 public class Singleton3 { private static Map<String,Singleton3> map = new HashMap<String,Singleton3>(); static{ Singleton3 single = new Singleton3(); map.put(single.getClass().getName(), single); } //保護(hù)的默認(rèn)構(gòu)造子 protected Singleton3(){} //靜態(tài)工廠方法,返還此類惟一的實例 public static Singleton3 getInstance(String name) { if(name == null) { name = Singleton3.class.getName(); System.out.println("name == null"+"--->name="+name); } if(map.get(name) == null) { try { map.put(name, (Singleton3) Class.forName(name).newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } return map.get(name); } //一個示意性的商業(yè)方法 public String about() { return "Hello, I am RegSingleton."; } public static void main(String[] args) { Singleton3 single3 = Singleton3.getInstance(null); System.out.println(single3.about()); } }
如有疑問請留言或者到本站社區(qū)交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關(guān)文章
springboot3整合knife4j詳細(xì)圖文教程(swagger增強(qiáng))
開發(fā)api提供對應(yīng)的接口規(guī)范進(jìn)行聯(lián)調(diào)或并行開發(fā),api文檔管理必不可少,常用的Knife4j基于swagger(依賴已經(jīng)compile),可以進(jìn)行管理,下面這篇文章主要給大家介紹了關(guān)于springboot3整合knife4j的相關(guān)資料,需要的朋友可以參考下2024-03-03Java使用CountDownLatch實現(xiàn)網(wǎng)絡(luò)同步請求的示例代碼
CountDownLatch 是一個同步工具類,用來協(xié)調(diào)多個線程之間的同步,它能夠使一個線程在等待另外一些線程完成各自工作之后,再繼續(xù)執(zhí)行。被將利用CountDownLatch實現(xiàn)網(wǎng)絡(luò)同步請求,異步同時獲取商品信息組裝,感興趣的可以了解一下2023-01-01RecyclerChart動態(tài)屬性圖標(biāo)聯(lián)動數(shù)據(jù)動態(tài)加載詳解
這篇文章主要為大家介紹了RecyclerChart動態(tài)屬性圖標(biāo)聯(lián)動數(shù)據(jù)動態(tài)加載詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03Java如何使用JSR303校驗數(shù)據(jù)與自定義校驗注解
這篇文章主要介紹了Java如何使用JSR303校驗數(shù)據(jù)與自定義校驗注解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Java中比較器Comparator和Comparable的區(qū)別
這篇文章主要介紹了Java中比較器Comparator和Comparable的區(qū)別,我們在使用?Collections.sort()對鏈表進(jìn)行排序時,常常需要根據(jù)不同情況自定義排序規(guī)則,今天我們來看看比較器之間的區(qū)別,需要的朋友可以參考下2023-08-08SpringCloud HystrixDashboard服務(wù)監(jiān)控詳解
Hystrix Dashboard 是Spring Cloud中查看Hystrix實例執(zhí)行情況的一種儀表盤組件,支持查看單個實例和查看集群實例,本文將對其服務(wù)監(jiān)控學(xué)習(xí)2022-11-11