vs2017.unresolved external symbol __imp__fprintf&__imp____iob_func
轉自:https://www.cnblogs.com/garryfu/p/7976546.html
Java設計模式之單例模式詳解
在Java開發過程中,很多場景下都會碰到或要用到單例模式,在設計模式裡也是經常作為指導學習的熱門模式之一,相信每位開發同事都用到過。我們總是沿著前輩的足跡去做設定好的思路,往往沒去探究為何這麼做,所以這篇文章對單例模式做了詳解。
一、單例模式定義:
單例模式確保某個類只有一個例項,而且自行例項化並向整個系統提供這個例項。在計算機系統中,執行緒池、快取、日誌物件、對話方塊、印表機、顯示卡的驅動程式物件常被設計成單例。這些應用都或多或少具有資源管理器的功能。每臺計算機可以有若干個印表機,但只能有一個Printer Spooler,以避免兩個列印作業同時輸出到印表機中。每臺計算機可以有若干通訊埠,系統應當集中管理這些通訊埠,以避免一個通訊埠同時被兩個請求同時呼叫。總之,選擇單例模式就是為了避免不一致狀態,避免政出多頭。
二、單例模式特點:
1、單例類只能有一個例項。
2、單例類必須自己建立自己的唯一例項。
3、單例類必須給所有其他物件提供這一例項。
單例模式保證了全域性物件的唯一性,比如系統啟動讀取配置檔案就需要單例保證配置的一致性。
三、執行緒安全的問題
一方面在獲取單例的時候,要保證不能產生多個例項物件,後面會詳細講到五種實現方式;
另一方面,在使用單例物件的時候,要注意單例物件內的例項變數是會被多執行緒共享的,推薦使用無狀態的物件,不會因為多個執行緒的交替排程而破壞自身狀態導致執行緒安全問題,比如我們常用的VO,DTO等(區域性變數是在使用者棧中的,而且使用者棧本身就是執行緒私有的記憶體區域,所以不存線上程安全問題)。
四、單例模式的選擇
還記得我們最早使用的MVC框架Struts1中的action就是單例模式的,而到了Struts2就使用了多例。在Struts1裡,當有多個請求訪問,每個都會分配一個新執行緒,在這些執行緒,操作的都是同一個action物件,每個使用者的資料都是不同的,而action卻只有一個。到了Struts2,action物件為每一個請求產生一個例項,並不會帶來執行緒安全問題(實際上servlet容器給每個請求產生許多可丟棄的物件,但是並沒有影響到效能和垃圾回收問題,有時間會做下研究)。
五、實現單例模式的方式
1.餓漢式單例(立即載入方式)
// 餓漢式單例 public class Singleton1 { // 私有構造 private Singleton1() {} private static Singleton1 single = new Singleton1(); // 靜態工廠方法 public static Singleton1 getInstance() { return single; } }
餓漢式單例在類載入初始化時就建立好一個靜態的物件供外部使用,除非系統重啟,這個物件不會改變,所以本身就是執行緒安全的。
Singleton通過將構造方法限定為private避免了類在外部被例項化,在同一個虛擬機器範圍內,Singleton的唯一例項只能通過getInstance()方法訪問。(事實上,通過Java反射機制是能夠例項化構造方法為private的類的,那基本上會使所有的Java單例實現失效。此問題在此處不做討論,姑且閉著眼就認為反射機制不存在。)
2.懶漢式單例(延遲載入方式)
// 懶漢式單例 public class Singleton2 {
// 私有構造 private Singleton2() {}
private static Singleton2 single = null;
public static Singleton2 getInstance() { if(single == null){ single = new Singleton2(); } return single; } }
該示例雖然用延遲載入方式實現了懶漢式單例,但在多執行緒環境下會產生多個single物件,如何改造請看以下方式:
使用synchronized同步鎖
public class Singleton3 { // 私有構造 private Singleton3() {} private static Singleton3 single = null; public static Singleton3 getInstance() { // 等同於 synchronized public static Singleton3 getInstance() synchronized(Singleton3.class){ // 注意:裡面的判斷是一定要加的,否則出現執行緒安全問題 if(single == null){ single = new Singleton3(); } } return single; } }
在方法上加synchronized同步鎖或是用同步程式碼塊對類加同步鎖,此種方式雖然解決了多個例項物件問題,但是該方式執行效率卻很低下,下一個執行緒想要獲取物件,就必須等待上一個執行緒釋放鎖之後,才可以繼續執行。
public class Singleton4 { // 私有構造 private Singleton4() {} private static Singleton4 single = null; // 雙重檢查 public static Singleton4 getInstance() { if (single == null) { synchronized (Singleton4.class) { if (single == null) { single = new Singleton4(); } } } return single; } }
使用雙重檢查進一步做了優化,可以避免整個方法被鎖,只對需要鎖的程式碼部分加鎖,可以提高執行效率。
3.靜態內部類實現
public class Singleton6 { // 私有構造 private Singleton6() {} // 靜態內部類 private static class InnerObject{ private static Singleton6 single = new Singleton6(); } public static Singleton6 getInstance() { return InnerObject.single; } }
靜態內部類雖然保證了單例在多執行緒併發下的執行緒安全性,但是在遇到序列化物件時,預設的方式執行得到的結果就是多例的。這種情況不多做說明了,使用時請注意。
4.static靜態程式碼塊實現
public class Singleton6 { // 私有構造 private Singleton6() {} private static Singleton6 single = null; // 靜態程式碼塊 static{ single = new Singleton6(); } public static Singleton6 getInstance() { return single; } }
5.內部列舉類實現
public class SingletonFactory { // 內部列舉類 private enum EnmuSingleton{ Singleton; private Singleton8 singleton; //列舉類的構造方法在類載入是被例項化 private EnmuSingleton(){ singleton = new Singleton8(); } public Singleton8 getInstance(){ return singleton; } } public static Singleton8 getInstance() { return EnmuSingleton.Singleton.getInstance(); } } class Singleton8{ public Singleton8(){} }
以上就是本文要介紹的所有單例模式的理解和實現,相信這篇文章能讓大家更清楚的理解單例模式,希望大家有問題可以探討,多多指教!
備註:本文單例實現部分,例項原始碼參照《Java多執行緒程式設計核心技術》-(高洪巖)一書中第六章的學習案例撰寫。
獲取文章中完整程式碼示例可訪問github:https://github.com/fugaoyang/per-practice