設計模式-單例模式(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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...