国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

【設計模式篇】單例模式

2019-11-14 15:30:30
字體:
來源:轉載
供稿:網友
01 單例模式

 

  單例模式,只有一個實例存在于整個JVM中,保證只有一個實例,并可以被外界訪問。它是一種常用的設計模式之一。實現單例模式

的方法有很多種,然而需要考慮包括線程安全在內的一些因素。以下列舉了幾種典型的實現方法。

 

02 實現及問題

 

 方法一:懶漢式實現

  【懶漢式】私有化構造函數,創建靜態方法,提供單例引用,延遲加載。 重大缺陷:線程不安全,線程A希望能夠使用Singleton

實例,于是第一次調用靜態方法getInstance(),發現此時singleton==null,準備創建實例,突然CPU發生時間片切換,線程B也希望

調用該實例,此時A并未創建,因此singleton==null,B創建Singleton實例,執行完成后,線程A繼續執行,因為已經判斷過singleto

n==null,所以也創建了一個實例對象。 最終導致單例失敗。

 1 /** 2  * 懶漢式 寫法1 3  * @author Administrator 4  * 5  */ 6 public class Singleton { 7     PRivate Singleton(){ 8          9     }10     private static Singleton singleton=null;11     //靜態工廠方法12     public static Singleton getInstance(){13         if(singleton==null){14             singleton=new Singleton();15         }16         return singleton;17     }18 }

 方法二:餓漢式實現

   【餓漢式】懶漢式存在線程安全問題,無法在多線程下實現單例。餓漢式的方法就是可以在類加載時創建一個靜態實例。其缺點在

于類的初始化開銷大。然而卻是天生安全的。

 1 /** 2  * 餓漢式:類初始化時,實例化。 3  * @author Administrator 4  * 5  */ 6 public class Singleton3 { 7     private Singleton3(){ 8          9     }10     private static final Singleton3 singleton=new Singleton3();11     public static Singleton3 getInstance(){12         return singleton;13     }14 }

方法三:同步懶漢式1

  【synchronized】在靜態方法getInstance()前添加synchronized同步關鍵字實現方法同步, 重大缺點:執行效率低。每一次調

用getInstance()都需要加鎖,而加鎖是非常耗時的,應盡量避免。

 1 /** 2  * 同步懶漢式1,添加synchronized 3  * @author Administrator 4  * 5  */ 6 public class Singleton1 { 7     private Singleton1(){ 8          9     }10     private static Singleton1 singleton=null;11     //靜態工廠方法12     public static synchronized Singleton1 getInstance(){13         if(singleton==null){14             singleton=new Singleton1();15         }16         return singleton;17     }18 }

方法四:雙重檢測懶漢式

   雙重檢測:即在同步塊前后分別判斷實例是否不為空。可以減少同步數。只有當instance==null的時候,才需要進入同步塊,進行

加鎖操作。當已經創建后,則無需進入同步塊,即不需要加鎖操作,只有第一次需要創建實例對象時,多個線程才需要進入同步塊,節

時間。

 1 /** 2  * 雙重檢測懶漢式:雙重檢測,提高效率 3  * @author Administrator 4  * 5  */ 6 public class Singleton2 { 7     private Singleton2(){ 8          9     }10     private static volatile  Singleton2 singleton=null;11     //靜態工廠方法12     public static Singleton2 getInstance(){13         if(singleton==null){    //若該線程檢測到不為空,則不用創建,否則,進入同步塊14             synchronized(Singleton2.class){15                 if(singleton==null){    //多個線程開始同步,若一個線程創建成功,則其它后進入的線程都不用創建16                     singleton=new Singleton2();17                 }18             }19             20         }21         return singleton;22     }23 }

  在雙重檢測中,使用了 volatile 關鍵字,能夠保證訪問到的變量是最后修改的。為什么要加volatile關鍵字呢?因為在java中雙重

檢測很可能導致失敗的結果,得到一個存在但未初始化完成的實例。《Java與模式》的總結是:在Java編譯器中,Singleton類的初

化與instance變量賦值的順序不可預料。如果一個線程在沒有同步化的條件下讀取instance引用,并調用這個對象的方法的

話,可能會發現對象初始化過程尚未完成,從而造成崩潰

  其原因在于 Java虛擬機中實現的 無序寫入 問題。所謂無序寫入,JVM編譯器能用不同的順序從程序的解釋中生成指令,并且存儲

變量在寄存器中,而并非內存,處理器可以并行或者顛倒次序的執行指令。完成的結果與嚴格連續執行的結果一致。例如在雙重檢測中

singleton=new Singleton() 語句可能包含下列偽代碼表示的指令。

  ----------------------------------------------------------

  memory=allocate(); //1、分配內存

  memory=Singleton();  //2、初始化實例

  singleton=memory;  //3、將實例賦值給引用變量。

  ----------------------------------------------------------

  由于JVM中存在無序寫入 問題,那么很可能先執行語句3,再執行語句2。即如下所以:

  ----------------------------------------------------------

  memory=allocate();  //1、分配內存

  singleton=memory();  //2、將未初始化的實例復制給引用變量

  memory=Singleton();  //3、實例初始化

  ----------------------------------------------------------

  如果在執行語句2后,線程切換成另外一個線程,此時線程可知 instance!=null,然而此時該實例卻是未初始化的詳細解釋

參考文獻[1]。

  那么是否加了volatile關鍵字 雙重檢測就達到最優了呢?不是,因為volatile與synchronized有相當的含義,訪問變量時

同synchronized。且由于兩次檢測,相比synchronized block 效率更低一些。[1]

 

 方法五: 靜態內部類

   使用靜態內部類存放靜態實例對象。在加載Singleton類時,并不加載內部類,在調用getInstance()時調用內部類,靜態實例時,

加載靜態內部類和內部靜態實例對象。從而達到延時加載的目的。

 1 /** 2  * 懶漢式:通過靜態內部類實現方式,既實現了線程安全,又提高了效率 3  * @author Administrator 4  * 5  */ 6 public class Singleton4 { 7     private Singleton4(){ 8          9     }10     public static Singleton4 getInstance(){11         return Instance.singleton;    //當需要返回實例的時候,初始化創建靜態實例對象。12     }13     14     private static class Instance{    15         private static final Singleton4 singleton=new Singleton4();16     }17 }

 方法六:Enum枚舉實現

   在《Effective Java》中提到,從Java1.5發行版本起,只需編寫一個包含單個元素的枚舉類型,實現Singleton。

 1 /** 2  * 枚舉類型實現單例 3  * @author Administrator 4  * 5  */ 6 public class Singleton5 { 7     public enum Singleton{ 8         INSTANCE; 9         10         public void method(){11             //任意方法12         }13     }    14 15     public static void main(String[] args) {16         Singleton.INSTANCE.method();17     }18 }

  枚舉方法 簡潔明了,并提供序列化機制,不會在反序列化實例時創建新實例(在其他單例方法中如果要變成可序列化的,不僅要加

上implements Serializable,還需要提供readResolve方法,參加《effective Java》)。也不會受到反射機制的影響,是實現單例

模式的最佳方法。

03 梳理概念點

 

  反射機制破壞單例模式:通過反射機制 例如借助accessibleObject.setAccessible方法調用私有構造方法。如果要防止這種機制的

影響,需要在構造方法中添加判斷如果創建第二個實例則拋出異常。

04 感謝

 

  [1] Robin Hu的專欄   http://blog.csdn.net/hudashi/article/details/6949379

  [2] cantellow  http://cantellow.VEvb.com/blog/838473

  [3] junJZ_2008  http://jiangzhengjun.VEvb.com/blog/652440

  [4] zhoujy http://my.oschina.net/zhoujy/blog/134958

  [5] FinderCheng  http://devbean.blog.51cto.com/448512/203501/

    [6] 炸斯特  http://blog.csdn.net/jason0539/article/details/23297037

  [7] Joshua Bloch 著,楊春花,俞黎敏 譯 《Effective Java中文版》


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 岳西县| 林口县| 含山县| 万安县| 襄樊市| 厦门市| 定南县| 蚌埠市| 绥芬河市| 延长县| 读书| 肇源县| 临海市| 郴州市| 丰都县| 大足县| 青龙| 开阳县| 江门市| 泗阳县| 马关县| 霸州市| 平武县| 平谷区| 宾阳县| 巴林左旗| 寻甸| 忻城县| 白银市| 临西县| 苍南县| 仙游县| 普陀区| 大名县| 疏附县| 什邡市| 门源| 金沙县| 越西县| 阿尔山市| 舒兰市|