小白算法_楔子

前言

筆者屬于算法小白一枚,本系列文章屬于算法的學(xué)習(xí)筆記,也希望能給算法小小白起到些許的指引作用。如果有算法大佬不小心點(diǎn)了進(jìn)來,只能說一聲抱歉打擾了。

思考

作為本系列文章的楔子,我們今天不討論算法解題,而是來談?wù)勔粋€(gè)老朋友:單例模式。相信大家對(duì)單例是非常熟悉的,但是如果要讓你手寫單例呢?寫起來磕磕碰碰還是嫻熟流暢?你會(huì)寫幾種?分別有什么區(qū)別?現(xiàn)在大家都在往 kotlin 轉(zhuǎn),那么在 kotlin 里面使用 object 關(guān)鍵字實(shí)現(xiàn)的單例又采用的是哪種實(shí)現(xiàn)方式呢?筆者從事 android 開發(fā)多年,在幾個(gè)互聯(lián)網(wǎng)大廠的面試中,經(jīng)歷了兩次手寫單例的考驗(yàn),所以在這里以手寫單例為楔子,為后面的手寫算法起一個(gè)拋磚引玉的作用。

正文

什么是單例模式

在同一個(gè)進(jìn)程中,有些時(shí)候我們需要某個(gè)類同時(shí)只保留一個(gè)對(duì)象,這個(gè)時(shí)候就應(yīng)該考慮單例模式的設(shè)計(jì)。

單例模式的特點(diǎn)

  • 單例模式只能有一個(gè)實(shí)例對(duì)象
  • 單例模式必須創(chuàng)建自己的唯一實(shí)例
  • 單例模式必須對(duì)外部提供這一實(shí)例

單例模式的實(shí)現(xiàn)

餓漢式

public class Singleton {

    private static Singleton INSTANCE = new Singleton();

    private Singleton(){
    }

    public static Singleton getInstance(){
        return INSTANCE;
    }
}

優(yōu)點(diǎn):線程安全,使用沒有延遲。
缺點(diǎn):類加載即初始化實(shí)例,內(nèi)存浪費(fèi)。

面試官追問:為什么線程安全?

  1. 餓漢式本質(zhì)上是使用的是靜態(tài)變量。
  2. 在類加載的過程中,靜態(tài)變量就會(huì)進(jìn)行初始化。
  3. 靜態(tài)變量只會(huì)初始化一次,并且被所有的對(duì)象所共享。

懶漢式

public class Singleton {

    private volatile static Singleton INSTANCE = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}

優(yōu)點(diǎn):懶加載節(jié)省資源,線程安全。
缺點(diǎn):線程同步存在性能損耗

面試官追問:為什么采用雙重鎖檢驗(yàn)?為什么使用 volatile 關(guān)鍵字?

  1. 線程同步是存在性能損耗的,我們只需要在實(shí)際創(chuàng)建對(duì)象的時(shí)候進(jìn)行同步,而不需要同步多余的代碼。
  2. 第一重校驗(yàn)不為 null 的時(shí)候,直接返回對(duì)象。否則準(zhǔn)備創(chuàng)建對(duì)象,此時(shí)需要進(jìn)行線程同步。
  3. 在創(chuàng)建對(duì)象之前還需要進(jìn)行第二重校驗(yàn),是為了確保等待同步的線程不會(huì)重復(fù)創(chuàng)建對(duì)象。
  4. 使用 volatile 關(guān)鍵字是為了禁止指令重排,確保對(duì)象初始化完全。

靜態(tài)內(nèi)部類

public class Singleton {

    private Singleton() {
    }

    private static class SingletonHolder {
        private static Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

優(yōu)點(diǎn):懶加載節(jié)省資源,無性能損耗的線程安全

面試官追問:如何實(shí)現(xiàn)的懶加載?為什么線程安全

  1. 靜態(tài)內(nèi)部類只有被調(diào)用的時(shí)候才會(huì)加載,滿足懶加載。
  2. 靜態(tài)內(nèi)部類在加載的時(shí)候,jvm 會(huì)保證線程安全。

總結(jié)

單例模式的實(shí)現(xiàn)很多,我們這里只取精華,只講重點(diǎn)。最關(guān)鍵的是在面試過程中,你是否可以在純文本編輯的環(huán)境下嫻熟的手寫以上幾種不同的單例,然后從容的告訴面試官它們之間的區(qū)別,最后再承受住面試官深挖的幾個(gè)為什么。

借一句古語,與諸君共勉:紙上得來終覺淺,絕知此事要躬行。

一個(gè)小小的單例提醒了我們,在后面的算法學(xué)習(xí)中要腳踏實(shí)地的手寫算法解題,切記不可只做頭腦風(fēng)暴。在大廠的高級(jí)職位面試過程中,面試官最喜歡做的就是讓求職者手寫點(diǎn)代碼。不需要太多,也不需要太難,就已經(jīng)可以看出很多東西了。兩位求職者,一個(gè)手寫起來嫻熟流暢,代碼強(qiáng)壯,格式工整。另外一個(gè)先不說測(cè)試用例是否通過,寫的過程都磕磕碰碰。你是面試官,你會(huì)如何抉擇呢?

最后附上 kotlin 通過 object 關(guān)鍵字實(shí)現(xiàn)單例的字節(jié)碼反編譯出來的 java 源代碼,跟你想的是否一樣呢?

public final class Singleton {
   public static final Singleton INSTANCE;

   private Singleton() {
   }

   static {
      Singleton var0 = new Singleton();
      INSTANCE = var0;
   }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容