設計模式-單例模式(Singleton Pattern)

来源:http://www.cnblogs.com/coffeeSS/archive/2016/04/24/5428203.html
-Advertisement-
Play Games

本文由@呆代待殆原創,轉載請註明出處。 單例模式簡述 單例模式保證了我們的類只有一個實例,並且我們在任何時候都可以取得這個實例,其中保證我們的類有且僅有一個實例在某些時候是相當重要的事情,比如我們只需要一個線程池而不是兩個等等,但是我們也要註意,單例模式適用的情況比我們想象中的要少,所以請不要濫用這 ...


本文由@呆代待殆原創,轉載請註明出處。

 

單例模式簡述

單例模式保證了我們的類只有一個實例,並且我們在任何時候都可以取得這個實例,其中保證我們的類有且僅有一個實例在某些時候是相當重要的事情,比如我們只需要一個線程池而不是兩個等等,但是我們也要註意,單例模式適用的情況比我們想象中的要少,所以請不要濫用這個模式。

 

單例模式具有的一些特征

1,單例模式保證了我們的程式中有且僅有一個實例的存在。

2,我們在任何時候都能取得這個實例。

3,單例模式的構造方法是私有的,所以在不破壞這一私有條件的情況下單例類是不能作為父類存在的。

 

單例模式的定義與基本結構

單例模式只有一個類而已,所以實際上並不存在結構這一說= =,但是我們還是可以看一下它的定義。

定義:確保類有且僅有一個實例,並保證任何時候都能訪問這個實例。(這句話好像已經出現了好的次 = =)

 

單例模式的定義與結構都非常簡單,理解起來甚至不需要舉額外的例子,但是,真正去實現單例的時候我們還是有很多細節要註意的,那麼下麵我們就在實際的代碼中繼續研究吧。

 

單例模式的代碼實現(Java版)

代碼實現

 1 public class MySingleten {
 2     public static MySingleten instance=null;//指向實例的變數
 3     private MySingleten(){}//私有化構造函數,然別的代碼無法創建這個類的實例
 4     public static MySingleten getInstance(){//我們取得這個單例的方法。
 5         if(null==instance){
 6             instance=new MySingleten();
 7         }
 8         return instance;
 9     }
10     public void MyFunction(){//一般方法的代表
11         System.out.println("我是單例,這是我的方法");
12     }
13 }

這種實現方式我們一般叫它:懶漢式

為什麼呢?因為它直到第一次被調用的時候才會生成自己的實例(就像我們每次都要到交作業的時候才會開始寫作業一樣,總之就是懶唄= ̄ω ̄=)

 

註意:懶漢式是線程不安全的

舉例:想象一下情況,線程A執行到代碼的第5行,判斷成功,在準備進入第6行的時候CPU切換了,線程B恰好也執行這段代碼,這時很明顯instance還是==null的浴室線程B成功創建了一個instance的實例,結果,當CPU又切回線程A時,麻煩來了,線程A繼續執行第6行代碼又創建了一個instance的實例,並把原來的那個覆蓋了,這就很有可能導致意想不到的問題。

 

所以,我們必須想辦法解決這個問題,這裡我們提供以下幾種思路。

餓漢式:不再等到要使用的時候才創建,而是在程式開始的時候就創建好(對這個實例很饑渴的樣子,所以叫餓(chi)漢式)

 1 public class MySingleten {
 2     public static MySingleten instance=new MySingleten();//一開始就生成這個變數就不存線上程問題了
 3     private MySingleten(){}
 4     public static MySingleten getInstance(){
 5         return instance;
 6     }
 7     public void MyFunction(){
 8         System.out.println("我是單例,這是我的方法");
 9     }
10 }

 

synchronized方法:直接在懶漢式的getInstance方法前加上synchronized修飾符,這樣就能解決線程安全問題了,這個解決方法是最簡單的,但是效率卻非常低下,因為只有第一次創建實例的時候這個synchronized是有必要的,當實例創建完成後,這個synchronized就只剩下拖慢速的作用了。

 1 public class MySingleten {
 2     public static MySingleten instance=null;
 3     private MySingleten(){}
 4     public static synchronized  MySingleten getInstance(){
 5         if(null==instance){
 6             instance=new MySingleten();
 7         }
 8         return instance;
 9     }
10     public void MyFunction(){
11         System.out.println("我是單例,這是我的方法");
12     }
13 }

 

雙重加鎖方法:懶漢式的基礎上,我們可以用兩把鎖來分別控制單例的取得和創建,因為只需要在第一次創建單例的時候註意線程安全問題,那麼,我們在內層鎖上用synchronized來控制,在外層鎖上用 if(null==instance) 來判斷是否存在這個實例,這樣就省去了synchronized在後來浪費的同步時間

 1 public class MySingleten {
 2     public volatile static MySingleten instance=null;//註意這裡增加了volatile關鍵字
 3     private MySingleten() {}
 4     public static MySingleten getInstance() {
 5         if (null == instance) {// 外層鎖,判斷是否實例已經被創建
 6             synchronized (MySingleten.class) {// 內層鎖控制線程間同步,實例被創建後就沒有運行的機會了,省去了多餘的線程間同步成本
 7                 if(null==instance)//需要再次檢查,因為很有可能線程A在這裡時,線程B已經通過外層的if了。
 8                     instance = new MySingleten();
 9             }
10         }
11         return instance;
12     }
13     public void MyFunction() {
14         System.out.println("我是單例,這是我的方法");
15     }
16 }

 

靜態全局變數和單例模式的對比

1,靜態全局變數並不能保證對象是唯一的(既然你能創建這個靜態全局變數就說明這個類的構造函數並不是私有的)。

2,多餘的全局變數會造成命名空間的污染。

3,全局變數總是存在,會一直占用記憶體,而單例模式可實現訪問的時候再創建單例的實例。

4,單例模式產生的對象保存在堆里,但是靜態全局變數保存在棧里。

 

靜態成員和單例模式的對比

1,用都是靜態成員的類去模擬單例的話,它是不能實現別的介面的,這種用法脫離了面向對象的思想(除非這個類的應用與實現不需要面向對象的思想那麼你可以這麼做)。

2,靜態成員可以選擇性的將類裡面的東西分成需要保證唯一性的和不需要保證唯一性的,某些時候更加靈活。

 

java版本相容性提醒

1,Java1.2之前的垃圾回收機制是有bug的,會造成當單例實例在沒有全局引用的情況下被清除掉。

2,Java1.4之前許多JVM對於volatile的實現會導致雙重加鎖的方法失效

 

關於單例模式的一些爭議

如果你剛剛看完了我寫的博文並覺得又有了一點收穫而感到很開心的話(如果真的是這樣那我也會很開心的♪(^∇^*)),我覺得你可以先冷靜一下(大霧= =),單例設計模式在網路上是一個很有爭議的模式,有人覺得這個模式違反了太多的設計原則,有人覺他增加了代碼的耦合性等等,但有的時候我們又確實需要單例帶給我們的一些特性,本來博主想多看一些別人的文章後幫大家總結一下網路上關於這方面的討論,結果博主發現這些討論實在是太多了,而且比較雜,各種聲音都有,stackoverflow上有個類似的問題以"這個問題的回答是基於個人選擇而不是基於經驗的..."而被關閉了,所以博主覺得還是選一些博主覺得比較好的連接給各位,讓各位自行斟酌比較好。(相關討論的連接都放在了參考資料里,如果覺得不夠的話,可以直接google一下"singleton why bad")

 

 

參考資料:

1,《Head First 設計模式》

2,https://agiletribe.wordpress.com/2013/10/08/dont-abuse-singleton-pattern/ 關於何時才應該使用單例模式

3,https://www.cnblogs.com/seesea125/archive/2012/04/05/2433463.html關於為什麼不用靜態方法而要用單例模式

4,https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons關於單例模式爭議的討論

5,https://stackoverflow.com/questions/519520/difference-between-static-class-and-singleton-pattern關於單例模式和靜態類之間的討論


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 下載頁: http://www.rabbitmq.com/install-standalone-mac.html 1、下載頁面首部的文件(頁面下載可能比較慢,使用迅雷下載就好),之後解壓到一個合適的路徑(例如:/Users/enniu1/Desktop/zjg/)。 2、配置命令訪問路徑 cd ~ ...
  • 上一篇文末,提到非虛擬介面 NVI 的實現,即將虛函數聲明為保護型或私有型,藉由模板函數模式來實現 。 園友 @KillU 看的很仔細,提出了一個問題:虛函數是 private 類型,繼承可以麽? 答案是:完全可以 5 實現權和調用權 <Effective C++> 中給的解釋是: 重寫一個虛函數, ...
  • 圖片上傳 Index.php文件代碼: upload.php代碼: 圖片上傳步驟: 1:接收參數 2:判斷錯誤 3:判斷格式是否合法 4:判斷文件大小 5:判斷是是不是真正的圖片 6:判斷是否是http post提交 文件上傳 Index.php文件代碼: <!DOCTYPE html> <html ...
  • AWT事件處理基本概念 AWT事件處理過程中,主要涉及3類對象: ① Event(事件):用戶對組件的一個操作,稱之為一個事件,以類的形式出現,例如,鍵盤操作對應的事件類是KeyEvent。其實例在該事件發生時由系統自動產生。每一種事件都對應專門的監聽者。 ② Event Source(事件源):事 ...
  • 1. Spring MVC簡介 Spring MVC是java EE平臺請求驅動類型的輕量級Web框架,使用了MVC設計模式的思想,spring框架的主要優勢之一就是分層架構,分層架構允許選擇使用任何一個組件,同時也可以集成其它框架技術,例如:Struts2、Hibernate等 Spring框架具 ...
  • 獲取【下載地址】 QQ: 313596790 【免費支持更新】三大資料庫 mysql oracle sqlsever 更專業、更強悍、適合不同用戶群體【新錄針對本系統的視頻教程,手把手教開發一個模塊,快速掌握本系統】 A集成代碼生成器 [正反雙向(單表、主表、明細表、樹形表,開發利器)+快速構建表單 ...
  • 一.意圖 將一個複雜對象的構建與它的表示分離,使得同樣的構造過程可以創建不同的表示。 二.動機 一個複雜的對象的構造過程中,原料相同,可能會要求生產不同的產品,並且生產的產品種類還能夠方便的增加。Bulider模式期望將解析原材料的過程與利用原材料生產產品的過程分離開,以達到用戶不需要知道根據原材料 ...
  • 兩年前接觸到了微服務的概念,面對日益膨脹的系統感覺豁然開朗。之後的兩年逐步把系統按微服務的架構理念進行了重構,並將業務遷移到了新架構之上。感覺現在差不多是時候寫一篇關於微服務的總結文章了。 定義 在 Martin Fowler & James Lewis 的文章(參考[1])里給出了微服務架構的一個 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...