面試官真是搞笑筷畦!讓實現(xiàn)線程安全的單例,又不讓使用synchronized刺洒!

文章來源于公眾號程序員面試現(xiàn)場 鳖宾,作者面試現(xiàn)場

單例模式吼砂,是Java中比較常見的一個設(shè)計模式,也是我在面試時經(jīng)常會問到的一個問題鼎文。

經(jīng)過我的初步統(tǒng)計渔肩,基本上有60%左右的人可以說出2-4種單例的實現(xiàn)方式,有40%左右的人可以說出5-6種單例的實現(xiàn)方式漂问,只有20%左右的人能夠說出7種單例的實現(xiàn)赖瞒。

而只有不到1%的人能夠說出7種以上的單例實現(xiàn)。

其實蚤假,作為面試官栏饮,我大多數(shù)情況下之所以問單例模式,是因為這個題目可以問到很多知識點磷仰。

比如線程安全袍嬉、類加載機制、synchronized的原理灶平、volatile的原理伺通、指令重排與內(nèi)存屏障、枚舉的實現(xiàn)逢享、反射與單例模式罐监、序列化如何破壞單例、CAS瞒爬、CAS的ABA問題弓柱、Threadlocal等知識。

一般情況下侧但,只需要從單例開始問起矢空,大概就可以完成一場面試的整個流程,把我想問的東西都問完禀横,可以比較全面的了解一個面試者的水平屁药。

以下,是一次面試現(xiàn)場的還原柏锄,從單例模式開始:

Q:你知道怎么不使用synchronized和lock實現(xiàn)一個線程安全的單例嗎酿箭?
A:我知道,可以使用"靜態(tài)內(nèi)部類"實現(xiàn)趾娃。

靜態(tài)內(nèi)部類實現(xiàn)單例模式:

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
}

Q:除了靜態(tài)內(nèi)部類還會其他的方式嗎缭嫡?
A:還有就是兩種餓漢模式。

餓漢實現(xiàn)單例模式:

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}

餓漢變種實現(xiàn)單例模式:

public class Singleton {  
    private Singleton instance = null;  
    static {  
    instance = new Singleton();  
    }  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return this.instance;  
    }  
}

Q:那你上面提到的幾種都是線程安全的嗎茫舶?
A:是線程安全的
Q:那是如何做到線程安全的呢械巡?
A:應(yīng)該是因為我使用了static,然后類加載的時候就線程安全了吧?
Q:其實你說的并不完全對讥耗,因為以上幾種雖然沒有直接使用synchronized有勾,但是也是間接用到了。
(這里面根據(jù)回答情況會朝兩個不同的方向展開:1古程、類加載機制蔼卡、模塊化等;2挣磨、繼續(xù)深入問單例模式)

類加載過程的線程安全性保證
以上的靜態(tài)內(nèi)部類雇逞、餓漢等模式均是通過定義靜態(tài)的成員變量,以保證單例對象可以在類初始化的過程中被實例化茁裙。

這其實是利用了ClassLoader的線程安全機制塘砸。ClassLoader的loadClass方法在加載類的時候使用了synchronized關(guān)鍵字。
所以晤锥, 除非被重寫掉蔬,這個方法默認在整個裝載過程中都是線程安全的。所以在類加載過程中對象的創(chuàng)建也是線程安全的矾瘾。

Q:那還回到剛開始的問題女轿,你知道怎么不使用synchronized和lock實現(xiàn)一個線程安全的單例嗎?
(并不是故意窮追不舍壕翩,而是希望能可以引發(fā)面試者的更多思考)
A:額蛉迹、、放妈、那枚舉吧北救,枚舉也可以實現(xiàn)單例。

枚舉實現(xiàn)單例模式:

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

Q:那你知道枚舉單例的原理嗎大猛?如何保證線程安全的呢扭倾?
枚舉單例的線程安全問題
枚舉其實底層是依賴Enum類實現(xiàn)的淀零,這個類的成員變量都是static類型的挽绩,并且在靜態(tài)代碼塊中實例化的,和餓漢有點像驾中, 所以他天然是線程安全的唉堪。

Q:所以,枚舉其實也是借助了synchronized的肩民,那你知道哪種方式可以完全不使用synchronized的嗎唠亚?
A:en....我想想
Q:(過了一會他好像沒有思路)你知道CAS嗎?使用CAS可以實現(xiàn)單例嗎持痰?
(面試中灶搜,如果面試者對于鎖比較了解的話,那我大多數(shù)情況下都會繼續(xù)朝兩個方向深入問:1、鎖的實現(xiàn)原理割卖;2前酿、非鎖,如CAS鹏溯、ThreadLocal等)
A:哦罢维,我知道,CAS是一項樂觀鎖技術(shù)丙挽,當多個線程嘗試使用CAS同時更新一個變量時肺孵,只有其中一個線程能更新成功。

借助CAS(AtomicReference)實現(xiàn)單例模式:

public class Singleton {
    private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();

    private Singleton() {}

    public static Singleton getInstance() {
        for (;;) {
            Singleton singleton = INSTANCE.get();
            if (null != singleton) {
                return singleton;
            }

            singleton = new Singleton();
            if (INSTANCE.compareAndSet(null, singleton)) {
                return singleton;
            }
        }
    }
}

Q:使用CAS實現(xiàn)的單例有沒有什么優(yōu)缺點呀颜阐?
A:用CAS的好處在于不需要使用傳統(tǒng)的鎖機制來保證線程安全平窘,CAS是一種基于忙等待的算法,依賴底層硬件的實現(xiàn)凳怨,相對于鎖它沒有線程切換和阻塞的額外消耗初婆,可以支持較大的并行度。
Q:你說的好像是優(yōu)點猿棉?那缺點呢磅叛?

CAS實現(xiàn)的單例的缺點
CAS的一個重要缺點在于如果忙等待一直執(zhí)行不成功(一直在死循環(huán)中),會對CPU造成較大的執(zhí)行開銷萨赁。

另外弊琴,代碼中,如果N個線程同時執(zhí)行到 singleton = new Singleton();的時候杖爽,會有大量對象被創(chuàng)建敲董,可能導(dǎo)致內(nèi)存溢出。
Q:好的慰安,除了使用CAS以外腋寨,你還知道有什么辦法可以不使用synchronized實現(xiàn)單例嗎?
A:這回真的不太知道了化焕。
Q:(那我再提醒他一下吧)可以考慮下ThreadLocal萄窜,看看能不能實現(xiàn)?
(面試者沒有思路的時候撒桨,我?guī)缀醵紩茸鲆幌绿嵝巡榭蹋瑢嵲跊]有思路再換下一個問題)
A:ThreadLocal?這也可以嗎凤类?
Q:你先說下你理解的ThreadLocal是什么吧
(通過他的回答穗泵,貌似對這個思路有些疑惑,不著急谜疤。先問一個簡單的問題佃延,讓面試者放松一下现诀,找找自信,然后再繼續(xù)問)

ThreadLoacal
ThreadLocal會為每一個線程提供一個獨立的變量副本履肃,從而隔離了多個線程對數(shù)據(jù)的訪問沖突赶盔。對于多線程資源共享的問題,同步機制(synchronized)采用了“以時間換空間”的方式榆浓,而ThreadLocal采用了“以空間換時間”的方式于未。

同步機制僅提供一份變量,讓不同的線程排隊訪問陡鹃,而ThreadLocal為每一個線程都提供了一份變量烘浦,因此可以同時訪問而互不影響。
Q:那理論上是不是可以使用ThreadLocal來實現(xiàn)單例呢萍鲸?
A:應(yīng)該也是可行的闷叉。

使用ThreadLocal實現(xiàn)單例模式:

public class Singleton {
     private static final ThreadLocal<Singleton> singleton =
     new ThreadLocal<Singleton>() {
         @Override
         protected Singleton initialValue() {
            return new Singleton();
         }
     };
     public static Singleton getInstance() {
        return singleton.get();
     }
     
     private Singleton() {}
}

Q:嗯嗯,好的脊阴,那有關(guān)單例模式的實現(xiàn)的問題我就問的差不多了握侧。
(ThreadLocal這種寫法主要是考察面試者對于ThreadLocal的理解,以及是否可以把知識活學(xué)活用嘿期,但是實際上品擎,這種所謂的"單例",其實失去了單例的意義...)
(但是說實話备徐,能回答到這一題的人很少萄传,大多數(shù)面試者基本上在前面幾道題就已經(jīng)沒有思路了,大多數(shù)情況下根本不會問到這個問題就要改方向了)
A:(心中竊喜)嗯嗯蜜猾,學(xué)習到很多秀菱,感謝
Q:那...你知道如何破壞單例嗎?
(單例問題蹭睡,必問的一個衍菱。通過這個引申到序列化和反射的相關(guān)知識)
A:(額....)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市肩豁,隨后出現(xiàn)的幾起案子脊串,更是在濱河造成了極大的恐慌,老刑警劉巖蓖救,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洪规,死亡現(xiàn)場離奇詭異印屁,居然都是意外死亡循捺,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門雄人,熙熙樓的掌柜王于貴愁眉苦臉地迎上來从橘,“玉大人念赶,你說我怎么就攤上這事∏×Γ” “怎么了叉谜?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長踩萎。 經(jīng)常有香客問我停局,道長,這世上最難降的妖魔是什么香府? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任董栽,我火速辦了婚禮,結(jié)果婚禮上企孩,老公的妹妹穿的比我還像新娘锭碳。我一直安慰自己,他們只是感情好勿璃,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布擒抛。 她就那樣靜靜地躺著,像睡著了一般补疑。 火紅的嫁衣襯著肌膚如雪歧沪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天莲组,我揣著相機與錄音槽畔,去河邊找鬼。 笑死胁编,一個胖子當著我的面吹牛厢钧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嬉橙,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼早直,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了市框?” 一聲冷哼從身側(cè)響起霞扬,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎枫振,沒想到半個月后喻圃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡粪滤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年斧拍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杖小。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡肆汹,死狀恐怖愚墓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情昂勉,我是刑警寧澤浪册,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站岗照,受9級特大地震影響村象,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜攒至,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一煞肾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嗓袱,春花似錦籍救、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至梧却,卻和暖如春奇颠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背放航。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工烈拒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人广鳍。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓荆几,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赊时。 傳聞我的和親對象是個殘疾皇子吨铸,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354