摘要:如此便可使得這一實(shí)現(xiàn)方式能夠同時(shí)具備線程安全延遲加載以及節(jié)省大量同步判斷資源等優(yōu)勢(shì),可以說(shuō)是單例模式的最佳實(shí)現(xiàn)了
單例模式(Singleton)是一種使用率非常高的設(shè)計(jì)模式,其主要目的在于保證某一類在運(yùn)行期間僅被創(chuàng)建一個(gè)實(shí)例,并為該實(shí)例提供了一個(gè)全局訪問(wèn)方法,通常命名為getInstance()方法。單例模式的本質(zhì)簡(jiǎn)言之即是:
控制實(shí)例數(shù)目
以Java為例,單例模式通常可分為餓漢式和懶漢式兩種常規(guī)實(shí)現(xiàn)方式
餓漢式單例實(shí)現(xiàn)餓漢式顧名思義,就是對(duì)類實(shí)例(食物?)的需求非常強(qiáng)烈,因此,在裝載該單例類的時(shí)候就會(huì)創(chuàng)建類實(shí)例。如下
public class Singleton { /** * 裝載時(shí)即創(chuàng)建類實(shí)例,并保存在類變量instance中 * 加上static關(guān)鍵詞使得該變量能在getInstance()靜態(tài)方法中使用 */ private static Singleton instance = new Singleton(); /** * 私有化構(gòu)造方法,使外部無(wú)法通過(guò)構(gòu)造方法構(gòu)造除instance外的類實(shí)例 * 從而達(dá)到單例模式控制類實(shí)例數(shù)目的目的 */ private Singleton() { } /** * 類實(shí)例的全局訪問(wèn)方法 * 加上static關(guān)鍵詞使得外部可以通過(guò)類名直接調(diào)用該方法獲取類實(shí)例 * @return 單例類實(shí)例 */ public static Singleton getInstance() { // 由于類實(shí)例在類裝載時(shí)已被創(chuàng)建并保存在instance中,因此可直接返回 return instance; } }
事實(shí)上,在Android開(kāi)發(fā)中,Android Studio提供了一個(gè)直接創(chuàng)建單例類的功能(File->new->Singleton),該功能自動(dòng)生成的單例類正是采用了餓漢式的實(shí)現(xiàn)方式
懶漢式單例實(shí)現(xiàn)說(shuō)到懶,我們自然而然會(huì)想到拖延癥這一惡習(xí),這一點(diǎn)和懶漢式的單例實(shí)現(xiàn)方式相似,這一實(shí)現(xiàn)方式會(huì)一直等到真正需要使用對(duì)象實(shí)例的時(shí)候再去創(chuàng)建該實(shí)例。如下
public class Singleton { /** * 裝載時(shí)不創(chuàng)建類實(shí)例,但需要利用一個(gè)類變量去保存后續(xù)創(chuàng)建的類實(shí)例 * 添加static關(guān)鍵詞使得該變量能在getInstance()靜態(tài)方法中使用 */ private static Singleton instance = null; /** * 私有化構(gòu)造方法,使外部無(wú)法通過(guò)構(gòu)造方法構(gòu)造除instance外的類實(shí)例 * 從而達(dá)到單例模式控制類實(shí)例數(shù)目的目的 */ private Singleton() { } /** * 類實(shí)例的全局訪問(wèn)方法 * 添加static關(guān)鍵詞使得外部可以通過(guò)類名直接調(diào)用該方法獲取類實(shí)例 * @return 單例類實(shí)例 */ public static Singleton getInstance() { // 如果instance未被初始化,則初始化該類實(shí)例 if (instance == null) { instance = new Singleton(); } return instance; } }
事實(shí)上,雖然我們前面拿拖延癥來(lái)與懶漢式做類比,但懶漢式的拖延卻是實(shí)際開(kāi)發(fā)中的一種較為常見(jiàn)的節(jié)省資源的方式,即延遲加載思想。這一思想的核心在于直到需要使用某些資源或數(shù)據(jù)時(shí)再去加載該資源或獲取該數(shù)據(jù),這樣可以盡可能地節(jié)省使用前的內(nèi)存空間
線程安全的懶漢式單例實(shí)現(xiàn)不難分析出,當(dāng)外部多個(gè)線程同時(shí)想要獲取單例類實(shí)例時(shí),上述懶漢式實(shí)現(xiàn)方式便很容易導(dǎo)致并發(fā)問(wèn)題。通常有如下幾種改進(jìn)方式
添加synchronized關(guān)鍵詞.... public static synchronized Singleton getInstance() { ....
這種改進(jìn)方式是最簡(jiǎn)單的,但由于外部每次調(diào)用getInstance()方法時(shí)均需進(jìn)行判斷,因此該方式也是效率較低的
利用雙重檢查加鎖機(jī)制雙重檢查加鎖機(jī)制分為如下兩重檢查
在程序每次調(diào)用getInstance()方法時(shí)先不進(jìn)行同步,而是在進(jìn)入該方法后再去檢查類實(shí)例是否存在,若不存在則進(jìn)入接下來(lái)的同步代碼塊
進(jìn)入同步代碼塊后將再次檢查類實(shí)例是否存在,若不存在則創(chuàng)建一個(gè)新的實(shí)例
這樣一來(lái),就只需要在類實(shí)例初始化時(shí)進(jìn)行一次同步判斷即可,而非每次調(diào)用getInstance()方法時(shí)都進(jìn)行同步判斷,大大節(jié)省了時(shí)間,具體實(shí)現(xiàn)如下
public class Singleton { /** * 裝載時(shí)不創(chuàng)建類實(shí)例,但需要利用一個(gè)類變量去保存后續(xù)創(chuàng)建的類實(shí)例 * 添加volatile關(guān)鍵詞使其不會(huì)被本地線程緩存,保證線程能正確處理 * 添加static關(guān)鍵詞使得該變量能在getInstance()靜態(tài)方法中使用 */ private volatile static Singleton instance = null; /** * 私有化構(gòu)造方法,使外部無(wú)法通過(guò)構(gòu)造方法構(gòu)造除instance外的類實(shí)例 * 從而達(dá)到單例模式控制類實(shí)例數(shù)目的目的 */ private Singleton() { } /** * 類實(shí)例的全局訪問(wèn)方法 * 添加static關(guān)鍵詞使得外部可以通過(guò)類名直接調(diào)用該方法獲取類實(shí)例 * @return 單例類實(shí)例 */ public static Singleton getInstance() { // 第一重檢查:如果instance未被初始化,則進(jìn)入同步代碼塊 if (instance == null) { // 同步代碼塊,保證線程安全 synchronized (Singleton.class) { // 第二重檢查:如果instance未被初始化,則初始化該類實(shí)例 if (instance == null) { instance = new Singleton(); } } } return instance; } }利用Java緩存思想實(shí)現(xiàn)的單例實(shí)現(xiàn)
public class Singleton { // 類實(shí)例緩存KEY值 private static final String KEY = "CACHE"; // 類實(shí)例緩存容器 private static Mapmap = new HashMap<>(); /** * 私有化構(gòu)造方法,使外部無(wú)法通過(guò)構(gòu)造方法構(gòu)造除instance外的類實(shí)例 * 從而達(dá)到單例模式控制類實(shí)例數(shù)目的目的 */ private Singleton() { } /** * 類實(shí)例的全局訪問(wèn)方法 * 添加static關(guān)鍵詞使得外部可以通過(guò)類名直接調(diào)用該方法獲取類實(shí)例 * @return 單例類實(shí)例 */ public static Singleton getInstance() { // 嘗試從緩存容器中獲取類實(shí)例 Singleton instance = map.get(KEY); // 未能獲取類實(shí)例,則初始化該實(shí)例,并將其緩存至容器中 if (instance == null) { instance = new Singleton(); map.put(KEY, instance); } return instance; } }
上述實(shí)現(xiàn)方式暫未考慮線程安全問(wèn)題。事實(shí)上,利用緩存來(lái)實(shí)現(xiàn)的單例模式其最大的優(yōu)點(diǎn)在于對(duì)單例模式進(jìn)行擴(kuò)展。我們自然而然地可以想到這么一種情況,既然在實(shí)際開(kāi)發(fā)中經(jīng)常需要保證某個(gè)類只能被創(chuàng)建一個(gè)實(shí)例,那么,會(huì)不會(huì)出現(xiàn)保證某個(gè)類只能被創(chuàng)建兩個(gè)或多個(gè)實(shí)例這種需求呢?對(duì)于這項(xiàng)需求,我們首先可以想到,上述實(shí)現(xiàn)方式中所建立的緩存容器是可以存儲(chǔ)多個(gè)類實(shí)例的,利用這一特點(diǎn),只需考慮一個(gè)問(wèn)題,即外部調(diào)用時(shí)到底需要為其返回哪一個(gè)實(shí)例,便可實(shí)現(xiàn)“雙例模式”以及“多例模式”(原諒我為它們?nèi)×艘恍┢婀值拿郑┝耍唧w實(shí)現(xiàn)如下
public class Singleton { // 可創(chuàng)建的最大類實(shí)例數(shù),這里以“雙例模式”為例 private static final int MAX = 2; // 類實(shí)例緩存KEY值 private static final String KEY = "CACHE"; // 當(dāng)前正在使用的實(shí)例序號(hào) private static int index = 1; // 類實(shí)例緩存容器 private static Map單例模式的最佳實(shí)現(xiàn)map = new HashMap<>(); /** * 私有化構(gòu)造方法,使外部無(wú)法通過(guò)構(gòu)造方法構(gòu)造除instance外的類實(shí)例 * 從而達(dá)到單例模式控制類實(shí)例數(shù)目的目的 */ private Singleton() { } /** * 類實(shí)例的全局訪問(wèn)方法 * 添加static關(guān)鍵詞使得外部可以通過(guò)類名直接調(diào)用該方法獲取類實(shí)例 * @return 單例類實(shí)例 */ public static Singleton getInstance() { // 嘗試從緩存容器中獲取第index個(gè)類實(shí)例 String key = KEY + index; Singleton instance = map.get(key); // 未能獲取類實(shí)例,則初始化該實(shí)例,并將其緩存至容器相應(yīng)index中 if (instance == null) { instance = new Singleton(); map.put(key, instance); } // 這里以最基本的順序調(diào)用為例,其他復(fù)雜調(diào)度方式不加討論,具體調(diào)用方式如下 // index++,以在下一次調(diào)用中獲取下一個(gè)類實(shí)例,當(dāng)達(dá)到類實(shí)例數(shù)上限時(shí),重新獲取第一個(gè)類實(shí)例 if ((++index) > MAX) { index = 1; } return instance; } }
綜合而言,上述實(shí)現(xiàn)方式都或多或少地存在諸如線程不安全、無(wú)法做到延遲加載等小缺陷。這里給出一個(gè)可以稱得上完美的最佳解決方案
Lazy Initialization Holder Class 模式
這一方案的核心在于Java的類級(jí)內(nèi)部類(即使用static關(guān)鍵詞修飾的內(nèi)部類,否則稱之為對(duì)象級(jí)內(nèi)部類)以及多線程缺省同步鎖,先來(lái)看看具體實(shí)現(xiàn)
public class Singleton { /** * 類級(jí)內(nèi)部類,用于緩存類實(shí)例 * 該類將在被調(diào)用時(shí)才會(huì)被裝載,從而實(shí)現(xiàn)了延遲加載 * 同時(shí)由于instance采用靜態(tài)初始化的方式,因此JVM能保證其線程安全性 */ private static class Instance { private static Singleton instance = new Singleton(); } /** * 私有化構(gòu)造方法,使外部無(wú)法通過(guò)構(gòu)造方法構(gòu)造除instance外的類實(shí)例 * 從而達(dá)到單例模式控制類實(shí)例數(shù)目的目的 */ private Singleton() { } /** * 類實(shí)例的全局訪問(wèn)方法 * 添加static關(guān)鍵詞使得外部可以通過(guò)類名直接調(diào)用該方法獲取類實(shí)例 * @return 單例類實(shí)例 */ public static Singleton getInstance() { return Instance.instance; } }
在前面提到的餓漢式實(shí)現(xiàn)方式中,我們利用Java的靜態(tài)初始化、借由JVM實(shí)現(xiàn)了線程安全,因此這里同樣采用了這種方式。而另一方面,為了避免餓漢式實(shí)現(xiàn)中無(wú)法進(jìn)行延遲加載的缺陷,我們構(gòu)造了一個(gè)類級(jí)內(nèi)部類來(lái)緩存類實(shí)例,由于該類只會(huì)在通過(guò)getInstance()方法去調(diào)用時(shí)才會(huì)被系統(tǒng)裝載,換言之,只有初次調(diào)用getInstance()方法時(shí)才會(huì)去初始化類實(shí)例,因此也實(shí)現(xiàn)了延遲加載這一功能。如此便可使得這一實(shí)現(xiàn)方式能夠同時(shí)具備線程安全、延遲加載以及節(jié)省大量同步判斷資源等優(yōu)勢(shì),可以說(shuō)是單例模式的最佳實(shí)現(xiàn)了
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/67780.html
摘要:好,看看大家喜聞樂(lè)見(jiàn)的并發(fā)場(chǎng)景下,這種簡(jiǎn)易的寫法會(huì)出現(xiàn)什么問(wèn)題兩個(gè)線程和同時(shí)訪問(wèn),它們都覺(jué)得判斷成立,分別執(zhí)行了步驟,成功創(chuàng)建出對(duì)象但是,我們通篇都在聊單例啊,和的玩法無(wú)疑很不單例問(wèn)題分析出來(lái)了,而解決上并不復(fù)雜讓線程同步就好。 單例的用處 如果你看過(guò)設(shè)計(jì)模式,肯定會(huì)知道單例模式,實(shí)際上這是我能默寫出代碼的第一個(gè)設(shè)計(jì)模式,雖然很長(zhǎng)一段時(shí)間我并不清楚單例具體是做什么用的。這里簡(jiǎn)單提一下單...
摘要:在面向?qū)ο蟮恼Z(yǔ)言中,比如,等,單例模式通常是定義類時(shí)將構(gòu)造函數(shù)設(shè)為,保證對(duì)象不能在外部被出來(lái),同時(shí)給類定義一個(gè)靜態(tài)的方法,用來(lái)獲取或者創(chuàng)建這個(gè)唯一的實(shí)例。 萬(wàn)事開(kāi)頭難,作為正經(jīng)歷菜鳥賽季的前端player,已經(jīng)忘記第一次告訴自己要寫一些東西出來(lái)是多久以的事情了。。。如果,你也和我一樣,那就像我一樣,從現(xiàn)在開(kāi)始,從看到這篇文章開(kāi)始,打開(kāi)電腦,敲下你的第一篇文章(或者任何形式的文字)吧。 ...
摘要:但由于這里僅僅是實(shí)現(xiàn)一個(gè),因此存儲(chǔ)功能僅通過(guò)一個(gè)單例類來(lái)模擬實(shí)現(xiàn)。 本文旨在通過(guò)重寫GridView,配合系統(tǒng)彈窗實(shí)現(xiàn)仿今日頭條的頻道編輯頁(yè)面 注:由于代碼稍長(zhǎng),本文僅列出關(guān)鍵部分,完整工程請(qǐng)參見(jiàn)【https://github.com/G9YH/YHChannelEdit】 在開(kāi)始講解盜版的實(shí)現(xiàn)方案前,讓我們先來(lái)看看正版與盜版的實(shí)際使用效果對(duì)比,首先是正版 showImg(https:...
閱讀 25629·2021-09-29 09:41
閱讀 4787·2021-09-10 11:20
閱讀 1918·2021-09-09 09:32
閱讀 1881·2019-08-30 15:44
閱讀 3192·2019-08-29 17:13
閱讀 2809·2019-08-29 14:14
閱讀 2061·2019-08-29 14:11
閱讀 3221·2019-08-29 12:36