面试官:不使用synchronized和lock,如何实现一个线程安全的单例?
單例,大家肯定都不陌生,這是Java中很重要的一個設計模式。稍微了解一點單例的朋友也都知道實現單例是要考慮并發問題的,一般情況下,我們都會使用synchronized來保證線程安全。
那么,如果有這樣一道面試題:不使用synchronized和lock,如何實現一個線程安全的單例? 你該如何回答?
C類應聘者:可以使用餓漢模式實現單例。如:
public?class?Singleton?{?private?static?Singleton instance =?new?Singleton();private?Singleton?(){}public?static?Singleton?getInstance()?{return?instance;} }還有部分程序員可以想到餓漢的變種:
public?class?Singleton?{private?Singleton instance =?null;static?{instance =?new?Singleton();}private?Singleton?(){}public?static?Singleton?getInstance()?{return?this.instance;}}使用static來定義靜態成員變量或靜態代碼,借助Class的類加載機制實現線程安全單例。
面試官:除了這種以外,還有其他方式嗎?
B類應聘者:
除了以上兩種方式,還有一種辦法,就是通過靜態內部類來實現,代碼如下:
public?class?Singleton?{private?static?class?SingletonHolder?{private static final Singleton INSTANCE = new Singleton();}private Singleton (){}public static final Singleton getInstance() {return SingletonHolder.INSTANCE;}}這種方式相比前面兩種有所優化,就是使用了lazy-loading。Singleton類被裝載了,但是instance并沒有立即初始化。因為SingletonHolder類沒有被主動使用,只有顯示通過調用getInstance方法時,才會顯示裝載SingletonHolder類,從而實例化instance。
面試官:除了這種以外,還有其他方式嗎?
A類應聘者:
除了以上方式,還可以使用枚舉的方式,如:
public?enum?Singleton {INSTANCE;public?void?whateverMethod()?{}}這種方式是Effective Java作者Josh Bloch 提倡的方式,它不僅能避免多線程同步問題,而且還能防止反序列化重新創建新的對象,可謂是很堅強的壁壘。
面試官:以上幾種答案,其實現原理都是利用借助了類加載的時候初始化單例。即借助了ClassLoader的線程安全機制。
所謂ClassLoader的線程安全機制,就是ClassLoader的loadClass方法在加載類的時候使用了synchronized關鍵字。也正是因為這樣, 除非被重寫,這個方法默認在整個裝載過程中都是同步的,也就是保證了線程安全。
所以,以上各種方法,雖然并沒有顯示的使用synchronized,但是還是其底層實現原理還是用到了synchronized。
面試官:除了這種以外,還有其他方式嗎?
A類應聘者:
還可以使用Java并發包中的Lock實現
面試官:本質上還是在使用鎖,不使用鎖的話,有辦法實現線程安全的單例嗎?
A+類面試者:
有的,那就是使用CAS。
CAS是項樂觀鎖技術,當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程并不會被掛起,而是被告知這次競爭中失敗,并可以再次嘗試。實現單例的方式如下:
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;}}} }面試官:這種方式實現的單例有啥優缺點嗎?
A++類面試者:
用CAS的好處在于不需要使用傳統的鎖機制來保證線程安全,CAS是一種基于忙等待的算法,依賴底層硬件的實現,相對于鎖它沒有線程切換和阻塞的額外消耗,可以支持較大的并行度。
CAS的一個重要缺點在于如果忙等待一直執行不成功(一直在死循環中),會對CPU造成較大的執行開銷。
另外,如果N個線程同時執行到singleton = new Singleton();的時候,會有大量對象創建,很可能導致內存溢出。
面試官:你被錄取了!
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的面试官:不使用synchronized和lock,如何实现一个线程安全的单例?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 听说你,对薪酬待遇不太满意。。。
- 下一篇: ServiceMesh究竟解决什么问题?