程序员面试题精选100题(45)-Singleton(C/C++/C#)
題目:設(shè)計(jì)一個(gè)類,我們只能生成該類的一個(gè)實(shí)例。
分析:只能生成一個(gè)實(shí)例的類是實(shí)現(xiàn)了Singleton模式的類型。
由于設(shè)計(jì)模式在面向?qū)ο蟪绦蛟O(shè)計(jì)中起著舉足輕重的作用,在面試過(guò)程中很多公司都喜歡問(wèn)一些與設(shè)計(jì)模式相關(guān)的問(wèn)題。在常用的模式中,Singleton是唯一一個(gè)能夠用短短幾十行代碼完整實(shí)現(xiàn)的模式。因此,寫一個(gè)Singleton的類型是一個(gè)很常見(jiàn)的面試題。
事實(shí)上,要讓一個(gè)類型是能創(chuàng)建一個(gè)實(shí)例不是一件很難的事情。我們可以把該類型的構(gòu)造函數(shù)設(shè)為private,這樣該類型的用戶就不能創(chuàng)建該類型的實(shí)例了。然后我們?cè)诮o類型中創(chuàng)建一個(gè)靜態(tài)實(shí)例。當(dāng)用戶需要該類型的實(shí)例時(shí),我們就返回這個(gè)實(shí)例。基于這個(gè)思路,我們可以用C#寫出如下代碼:
????
// We can only get an instance of the class Singleton1. The instance// is created when class Singleton1 is referenced at the first timepublic sealed class Singleton1{private Singleton1(){}private static Singleton1 instance = new Singleton1();public static Singleton1 Instance{get{return instance;}}}?
由于類Singleton1?的實(shí)例是一個(gè)靜態(tài)變量,因此它會(huì)在該類型的第一次引用的時(shí)候被創(chuàng)建,而不是第一次在調(diào)用Singleton1.get_Instance的時(shí)候被創(chuàng)建。如果我們此時(shí)并不需要該實(shí)例,那么我們就過(guò)早地初始化該實(shí)例,無(wú)論在內(nèi)存空間還是CPU時(shí)間上都是一種浪費(fèi)。
我們可以把上面的代碼稍作改動(dòng),就能實(shí)現(xiàn)在第一次調(diào)用Singleton_getInstance時(shí)候才會(huì)創(chuàng)建類型的唯一實(shí)例:
? ?
// We can only get an instance of the class Singleton2.// The instance is created when we need it explicitly.public sealed class Singleton2{private Singleton2(){}private static Singleton2 instance = null;public static Singleton2 Instance{get{if (instance == null)instance = new Singleton2();return instance;}}}?
我們?cè)趩尉€程環(huán)境下只能得到類型Singleton2的一個(gè)實(shí)例,但在多線程環(huán)境下情況就可能不同了。設(shè)想如果兩個(gè)線程同時(shí)運(yùn)行到語(yǔ)句if?(instance ==?null),而此時(shí)該實(shí)例的確沒(méi)有創(chuàng)建,那么兩個(gè)線程都會(huì)創(chuàng)建一個(gè)實(shí)例。此時(shí),類型Singleton2就不在滿足模式Singleton的要求了。
為了保證在多線程環(huán)境下我們還是只能得到類型的一個(gè)實(shí)例,我們應(yīng)該在判斷實(shí)例是否已經(jīng)創(chuàng)建,以及在實(shí)例還沒(méi)有創(chuàng)建的時(shí)候創(chuàng)建一個(gè)實(shí)例的語(yǔ)句上加一個(gè)同步鎖。我們把Singleton2稍作修改就得到了如下代碼:
? ?
// We can only get an instance of the class Singleton3,// even when there are multiple threads which are trying// to get an instance concurrently.public sealed class Singleton3{private Singleton3(){}private static readonly object syncObj = new object();private static Singleton3 instance = null;public static Singleton3 Instance{get{lock (syncObj){if (instance == null)instance = new Singleton3();}return instance;}}}?
說(shuō)明一下,由于C/C++沒(méi)有為線程同步提供直接的支持。為了讓代碼顯得簡(jiǎn)潔,而不是讓大量的代碼在實(shí)現(xiàn)同步鎖而偏離了實(shí)現(xiàn)Singleton的主題,本文的代碼用C#實(shí)現(xiàn)。
我們還是假設(shè)有兩個(gè)線程同時(shí)想創(chuàng)建一個(gè)實(shí)例。由于在一個(gè)時(shí)刻只能有一個(gè)線程能得到同步鎖。當(dāng)?shù)谝粋€(gè)線程加上鎖時(shí),第二個(gè)線程只能在等待。當(dāng)?shù)谝粋€(gè)線程發(fā)現(xiàn)實(shí)例還沒(méi)有創(chuàng)建時(shí),它創(chuàng)建出一個(gè)實(shí)例。接著第一個(gè)線程釋放同步鎖。此時(shí)第二個(gè)線程可以加上同步鎖,并運(yùn)行接下來(lái)的代碼。由于此時(shí)實(shí)例已經(jīng)被第一個(gè)線程創(chuàng)建出來(lái)了,第二個(gè)線程就不會(huì)重復(fù)創(chuàng)建實(shí)例了。于是保證了我們只能得到一個(gè)實(shí)例。
但是類型Singleton3還不是完美。由于我們每次調(diào)用Singleton3.get_Instance的時(shí)候,都會(huì)試圖加上一個(gè)同步鎖。由于加鎖是一個(gè)非常耗時(shí)的操作,在沒(méi)有必要的時(shí)候我們應(yīng)該盡量避免這樣的操作。
實(shí)際上,我們只是在實(shí)例還沒(méi)有創(chuàng)建之前需要加鎖操作,以保證只有一個(gè)線程創(chuàng)建出實(shí)例。而當(dāng)實(shí)例已經(jīng)創(chuàng)建之后,我們已經(jīng)不需要再做加鎖操作了。于是,我們可以把上述代碼再作進(jìn)一步的改進(jìn):
??
// We can only get an instance of the class Singleton4,// even when there are multiple threads which are trying// to get an instance concurrently. When the instance has// been created, we don't need the lock any more.public sealed class Singleton4{private Singleton4(){}private static object syncObj = new object();private static Singleton4 instance = null;public static Singleton4 Instance{get{if (instance == null){lock (syncObj){if (instance == null)instance = new Singleton4();}}return instance;}}}?
?我們只需要在最開(kāi)始調(diào)用Singleton4_getInstance(可能來(lái)自一個(gè)線程,也可能來(lái)自多個(gè)線程)的時(shí)候需要加鎖。當(dāng)實(shí)例已經(jīng)創(chuàng)建之后,我們就不再需要作加鎖操作,從而在后續(xù)調(diào)用Singleton4_getInstance時(shí)性能得到提升。
關(guān)于第一種寫法的更多,請(qǐng)參考http://en.wikipedia.org/wiki/Double-checked_locking。站在面試的角度,本文的分析已經(jīng)足夠應(yīng)付。但如果想展示更多對(duì)多線程編程的理解,更深入地了解這個(gè)問(wèn)題總是有益的。
本文已經(jīng)收錄到《劍指Offer——名企面試官精講典型編程題》一書中,有改動(dòng),書中的分析講解更加詳細(xì),增加了一種按需分配內(nèi)存的方法。歡迎關(guān)注。
博主何海濤對(duì)本博客文章享有版權(quán)。網(wǎng)絡(luò)轉(zhuǎn)載請(qǐng)注明出處http://zhedahht.blog.163.com/。整理出版物請(qǐng)和作者聯(lián)系。
總結(jié)
以上是生活随笔為你收集整理的程序员面试题精选100题(45)-Singleton(C/C++/C#)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 程序员面试题精选100题(42)-旋转数
- 下一篇: 程序员面试题精选100题(46)-对称子