数据结构与算法--实现Singleton模式
生活随笔
收集整理的這篇文章主要介紹了
数据结构与算法--实现Singleton模式
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
題目:設(shè)計(jì)一個(gè)類,我們只生成該類的一個(gè)實(shí)例。
- 只生成一個(gè)實(shí)例的類就是實(shí)現(xiàn)Singleton(單例)模式的類型。本題其實(shí)主要考察我們?cè)O(shè)計(jì)模式,因?yàn)槊嬖嚨臅r(shí)候先來一個(gè)簡(jiǎn)單的,并且喜歡面設(shè)計(jì)模式相關(guān)的題目,而且,在常用的設(shè)計(jì)模式中,Singleton是比較簡(jiǎn)單的,而且可以通過簡(jiǎn)潔的代碼來實(shí)現(xiàn)。所有Singleton是常見的面試題目。
以下解題思路
- 由于只生成一個(gè)實(shí)例,我們可以將構(gòu)造方法設(shè)置成私有構(gòu)造,使得其他方法無(wú)法通過實(shí)例創(chuàng)建。我們可以定義一個(gè)靜態(tài)實(shí)例,在需要的時(shí)候創(chuàng)建該實(shí)例,如下思路:
解法一:只適用單線程
//不好解法一 /*** @author liaojiamin* @Date:Created in 10:18 2020/10/27*/ public class Singleton {private Singleton(){}public static Singleton singleton = null;public static Singleton getInstance(){if(null == singleton){singleton = new Singleton();}return singleton;} }- 分析,以上代碼是在不考慮并發(fā)的情況下的簡(jiǎn)單單例模式,從下面幾點(diǎn)來保證了得到的實(shí)例是唯一的:
- 靜態(tài)實(shí)例:帶有static關(guān)鍵字的熟悉在每個(gè)類中都是唯一的(在class文件被加載到j(luò)vm中的準(zhǔn)備階段,方法區(qū)為這些類變量進(jìn)行內(nèi)存分配,并且進(jìn)行初始化。比如被static修飾的字段。非static修飾屬性會(huì)在類實(shí)例化時(shí)候在對(duì)內(nèi)存中分配存儲(chǔ)空間。因此類變了在class文件被加載的時(shí)候才有,并不受實(shí)例化的影響)
- 私有構(gòu)造方法限制客戶通過實(shí)例創(chuàng)建
- 提供getInstance唯一入口
- 以上代碼存在并發(fā)問題,用如下方法進(jìn)行檢測(cè):
解法二:并發(fā)安全,但是效率低
/*** @author liaojiamin* @Date:Created in 14:33 2020/10/27*/ public class SingletonOne {private SingletonOne(){}public static SingletonOne singletonOne;public static SingletonOne getInstance(){synchronized (SingletonOne.class){if(null == singletonOne){singletonOne = new SingletonOne();}return singletonOne;}} }- 區(qū)別在于獲取對(duì)象時(shí),用synchronized關(guān)鍵字進(jìn)行加鎖,同一時(shí)刻只能有一個(gè)線程執(zhí)行,等第一個(gè)線程創(chuàng)建完時(shí)間后。第一個(gè)線程釋放同步鎖,此時(shí)第二個(gè)線程可以加上同步鎖,并允許接下來的代碼。這個(gè)時(shí)候,實(shí)例已經(jīng)被第一個(gè)線程創(chuàng)建,所以第二個(gè)線程不會(huì)再重復(fù)創(chuàng)建實(shí)例,保證得到的是同一個(gè)實(shí)例(synchronized加鎖是一個(gè)非常耗時(shí)的操作,應(yīng)該盡量避免)
可行的解法:加同步鎖前后兩次判斷
- 我們可以優(yōu)化以上方法,只在我們需要的時(shí)候鏡像同步鎖,如下。
- 以上SingletonTwo中只有instance為null的時(shí)候,才需要加鎖。當(dāng)instance已經(jīng)創(chuàng)建出來后,無(wú)須加鎖。因?yàn)橹挥械谝淮蝘nstance為null,所以執(zhí)行的結(jié)果就是只有第一次的時(shí)候才會(huì)加鎖,其他時(shí)候都無(wú)需鎖,所有效率高得多。
- 兩次判斷的原因:
- 假設(shè)我們?nèi)サ敉綁K中的是否為null的判斷,有這樣一種情況,假設(shè)A線程和B線程都在同步塊外面判斷了synchronizedSingleton為null,結(jié)果A線程首先獲得了線程鎖,進(jìn)入了同步塊,然后A線程會(huì)創(chuàng)造一個(gè)實(shí)例,此時(shí)synchronizedSingleton已經(jīng)被賦予了實(shí)例,A線程退出同步塊,直接返回了第一個(gè)創(chuàng)造的實(shí)例,此時(shí)B線程獲得線程鎖,也進(jìn)入同步塊,此時(shí)A線程其實(shí)已經(jīng)創(chuàng)造好了實(shí)例,B線程正常情況應(yīng)該直接返回的,但是因?yàn)橥綁K里沒有判斷是否為null,直接就是一條創(chuàng)建實(shí)例的語(yǔ)句,所以B線程也會(huì)創(chuàng)造一個(gè)實(shí)例返回,此時(shí)就造成創(chuàng)造了多個(gè)實(shí)例的情況。
推薦的解法:利用靜態(tài)屬性
/*** @author liaojiamin* @Date:Created in 14:40 2020/10/27*/ public class SingletonThree {private SingletonThree(){}private static SingletonThree singletonThree = new SingletonThree();public static SingletonThree getInstance(){return singletonThree;} }- SingletonThree實(shí)現(xiàn)的方式簡(jiǎn)潔。我們初始化靜態(tài)變量singletonThree 時(shí)候創(chuàng)建一個(gè)實(shí)例。由于Java是在類加載的時(shí)候在方法區(qū)對(duì)靜態(tài)屬性分配內(nèi)存,并且只初始化一次,這樣我們就能保證只初始化一次singletonThree 。
- 因?yàn)镾ingletonThree 中singletonThree 的初始化并不是調(diào)用getInstance的時(shí)候創(chuàng)建的,而且在類加載的時(shí)候就已經(jīng)創(chuàng)建,我們使用的時(shí)候調(diào)用getInstance方法他其實(shí)是不會(huì)創(chuàng)建新的實(shí)例,所有他會(huì)提前創(chuàng)建好實(shí)例,不管你之后是否需要
最優(yōu)的解法:實(shí)現(xiàn)按需創(chuàng)建實(shí)例
/*** @author liaojiamin* @Date:Created in 15:45 2020/10/27*/ public class SingletonFour {private SingletonFour(){}private static class SingletonInstance{static SingletonFour singletonFour = new SingletonFour();}public static SingletonFour getInstance(){return SingletonInstance.singletonFour;} }- SingletonFour 中我們定義了一個(gè)私有的內(nèi)部類SingletonInstance我們利用:**內(nèi)部靜態(tài)類不會(huì)自動(dòng)初始化,只有調(diào)用靜態(tài)內(nèi)部類的方法,靜態(tài)域,或者構(gòu)造方法的時(shí)候才會(huì)加載靜態(tài)內(nèi)部類。**的特點(diǎn)來實(shí)現(xiàn)的此處的單例
- 由于靜態(tài)內(nèi)部類是私有的,只有我們?cè)谡{(diào)用getInstance方法的時(shí)候被用到,因此當(dāng)我們?cè)噲D通過屬性SingletonFour .getInstance得到SingletonFour 時(shí)候,會(huì)自動(dòng)調(diào)用內(nèi)部類SingletonInstance的靜態(tài)構(gòu)造方法創(chuàng)建實(shí)例,并初始化內(nèi)部類中的靜態(tài)變量 singletonFour
解法比較
- 以上五種實(shí)現(xiàn)方案中,第一張方法在多線程環(huán)境中不能正常工作,第二種線程安全的方法但是實(shí)際效率低下,都不是我們所期待的可運(yùn)行的解法。第三種方法中,我們通過兩次判斷加一次鎖確保在多線程環(huán)境能高效運(yùn)轉(zhuǎn)。第四種利用java靜態(tài)屬性的特性,確保值創(chuàng)建一個(gè)實(shí)例。第五種方法利用私有嵌套類型的特性,做到只在真正需要的時(shí)候才創(chuàng)建實(shí)例,提高空間使用率。第五種解法是最優(yōu)解。
下一篇: 數(shù)據(jù)結(jié)構(gòu)與算法–數(shù)組:二維數(shù)組中查找
總結(jié)
以上是生活随笔為你收集整理的数据结构与算法--实现Singleton模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 病毒性脑炎严重吗
- 下一篇: 数据结构与算法--数组:二维数组中查找