1、前言
1-1、 概述
設(shè)計模式 = 某類特定問題的解決方案,那么單例模式是解決什么問題的解決方案呢?
含義:單例 = 一個實例
解決的問題:在任何時間內(nèi)只有一個類實例存在的模式
解決方法:保證一個類只有一個實例化對象,并提供一個全局訪問入口
本質(zhì):控制實例的數(shù)量
注意:要合理的使用單例,避免單例成為瓶頸
英文:Singleton
類型:創(chuàng)建類模式
1-2、問題引入
模擬網(wǎng)站訪問數(shù)量統(tǒng)計功能:
結(jié)果:
1 2 3 4 | 是不是同一個網(wǎng)站? 不是 訪問量:1 訪問量:1 |
從結(jié)果看,兩個人訪問的不是一個網(wǎng)站實例,其實我們要實現(xiàn)的邏輯是,訪問同一個網(wǎng)站,計算訪問量,這顯然是不符合我們想要的
2、介紹
2-1、分析引入的問題
沖突:從上面的結(jié)果可以看出,網(wǎng)站計數(shù)器類操作的明顯不是同一個實例
目標(biāo):所有訪問者操作同一個網(wǎng)站計數(shù)器類
單例模式就是為了解決這類問題的解決方案:實現(xiàn)一個類只有一個實例化對象,并提供一個全局訪問入口
2-2、解決引入的問題
解決:改造一下網(wǎng)站計數(shù)器類的實現(xiàn)代碼
再來看一下結(jié)果:
1 2 3 4 | 是不是同一個網(wǎng)站? 是 訪問量:1 訪問量:2 |
這次是對的?。?!
2-3、實現(xiàn)原理
引入單例模式:一般實現(xiàn)方式
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class Singleton { //1. 創(chuàng)建私有變量 instance(用以記錄 Singleton 的唯一實例) //2. 內(nèi)部進行實例化 private static Singleton instance = new Singleton(); //3. 把類的構(gòu)造方法私有化,不讓外部調(diào)用構(gòu)造方法實例化 private Singleton() { } //4. 定義公有方法提供該類的全局唯一訪問點 //5. 外部通過調(diào)用getInstance()方法來返回唯一的實例 public static Singleton getInstance() { return instance; } } |
2-4、優(yōu)點、缺點
優(yōu)點:
1.在單例模式中,活動的單例只有一個實例,對單例類的所有實例化得到的都是相同的一個實例。這樣就防止其它對象對自己的實例化,確保所有的對象都訪問一個實例
2.單例模式具有一定的伸縮性,類自己來控制實例化進程,類在實例化進程上有相應(yīng)的伸縮性
3.提供了對唯一實例的訪問入口
4.由于在系統(tǒng)內(nèi)存中只存在一個對象,因此可以節(jié)約系統(tǒng)資源,當(dāng)需要頻繁創(chuàng)建和銷毀的對象時單例模式無疑可以提高系統(tǒng)的性能
5.允許可變數(shù)目的實例(可以根據(jù)實際情況需要,在單例模式的基礎(chǔ)上擴展做出雙例模式、多例模式)
6.避免對共享資源的多重占用
缺點:
1.不適用于變化的對象,如果同一類型的對象總是要在不同的用例場景發(fā)生變化,單例就會引起數(shù)據(jù)的錯誤,不能保存彼此的狀態(tài)
2.由于單利模式中沒有抽象層,因此單例類的擴展有很大的困難。
3.單例類的職責(zé)過重,在一定程度上違背了“單一職責(zé)原則”
4.濫用單例將帶來一些負面問題,如為了節(jié)省資源將數(shù)據(jù)庫連接池對象設(shè)計為的單例類,可能會導(dǎo)致共享連接池對象的程序過多而出現(xiàn)連接池溢出;如果實例化的對象長時間不被利用,系統(tǒng)會認為是垃圾而被回收,這將導(dǎo)致對象狀態(tài)的丟失
3、實現(xiàn)
3-1、餓漢模式、懶漢模式
餓漢模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package com.designmode; /** * 餓漢模式(最簡單的形式) */ public class Singleton { private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } } |
應(yīng)用場景:
要求直接在應(yīng)用啟動時加載并初始化
單例對象要求初始化速度非???/p>
懶漢模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package com.designmode; /** * 懶漢模式(最簡單的形式) */ public class Singleton { private static Singleton instance = null ; private Singleton() { } public static Singleton newInstance() { if (instance == null ){ instance = new Singleton(); } return instance; } } |
應(yīng)用場景:
單例初始化的操作耗時比較長(可以相應(yīng)縮短應(yīng)用啟動時間)
單例只是在某個特定場景的情況下才會被使用,即按需延遲加載單例
對比:
餓漢式:自動進行單例的初始化
懶漢式:有需要的時候才手動調(diào)用getInstance()進行單例的初始化操作
3-2、多線程下的實現(xiàn)
在多線程的情況下:
“餓漢式單例模式”:適用,因為JVM只會加載一次單例類
“懶漢式單例模式”:不適用,因為“懶漢式”在創(chuàng)建單例時是線程不安全的,多個線程可能會并發(fā)調(diào)用 getInstance 方法從而出現(xiàn)重復(fù)創(chuàng)建單例對象的問題
下面有幾個解決方案:
方案1:同步鎖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //使用同步鎖 synchronized (Singleton.class) 防止多線程同時進入造成instance被多次實例化 package com.designmode; public class Singleton { private static Singleton instance = null ; private Singleton() { } public static Singleton getInstance() { synchronized (Singleton. class ){ if (instance == null ){ instance = new Singleton(); } } return instance; } } |
方案2:雙重校驗鎖
1 2 3 4 5 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //在同步鎖的基礎(chǔ)上( synchronized (Singleton.class) 外)添加了一層if,這是為了在Instance已經(jīng)實例化后下次進入不必執(zhí)行 synchronized (Singleton.class) 獲取對象鎖,從而提高性能 package com.designmode; public class Singleton { private static Singleton instance = null ; private Singleton() { } public static Singleton getInstance() { if (instance == null ){ synchronized (Singleton. class ){ if (instance == null ){ instance = new Singleton(); } } } return instance; } } |
方案3:靜態(tài)內(nèi)部類
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //在JVM進行類加載的時候會保證數(shù)據(jù)是同步的,我們采用內(nèi)部類實現(xiàn):在內(nèi)部類里面去創(chuàng)建對象實例 //只要應(yīng)用中不使用內(nèi)部類 JVM 就不會去加載這個單例類,也就不會創(chuàng)建單例對象,從而實現(xiàn)“懶漢式”的延遲加載和線程安全。 package com.designmode; public class Singleton { //在裝載該內(nèi)部類時才會去創(chuàng)建單例對象 private static class Singleton2{ private static Singleton instance = new Singleton(); } private Singleton() { } public static Singleton getInstance() { return Singleton2.instance; } } |
方案4:枚舉類型
1 2 3 4 5 6 7 8 9 10 11 12 13 | //最簡潔、易用的單例實現(xiàn)方式,(《Effective Java》推薦) package com.designmode; public enum Singleton{ //定義一個枚舉的元素,它就是Singleton的一個實例 INSTANCE; private Singleton() { } public void doSomething(){ } } |
調(diào)用方式:
1 | Singleton.INSTANCE.doSomething(); |
4、總結(jié)
設(shè)計模式 = 某類特定問題的解決方案,那么單例模式是解決什么問題的解決方案呢?
含義:單例 = 一個實例
解決的問題:在任何時間內(nèi)只有一個類實例存在的模式
解決方法:保證一個類只有一個實例化對象,并提供一個全局訪問入口
本質(zhì):控制實例的數(shù)量
注意:要合理的使用單例,避免單例成為瓶頸
PS:源碼地址 https://github.com/JsonShare/DesignPattern/tree/master
PS:原文地址 http://www.cnblogs.com/JsonShare/p/7093947.html
如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!
如果標(biāo)題被標(biāo)記為轉(zhuǎn)載,轉(zhuǎn)載請注明原作者地址,詳見引用
版權(quán)聲明,轉(zhuǎn)載請注明出處:http://www.cnblogs.com/JsonShare
http://www.cnblogs.com/JsonShare/p/7093947.html