面試必問——設計模式之單例設計模式

相信大家面試的時候,經常會被問到,23種設計模式,你熟悉哪寫,手寫個單例啥的,當然了,這是對於小程序員的面試,大佬請忽略
單例模式呢,顧名思義就是這個對象只會創建一次。因此他的構造方法必須私有。
那麼這隻創建一次的對象等你用的時候,又怎麼用呢?肯定是需要一個方法返回提供同一個對象,也就是唯一的對象。

單例模式

單例模式(Singleton Pattern): 確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例,這個類稱爲單例類,它提供全局訪問的方法。單例模式是一種對象創建型模式。
單例模式有三個要點:

  1. 構造方法私有化;
  2. 實例化的變量引用私有化;
  3. 獲取實例的方法共有

因此代碼我們可以這樣寫(通過學習觀看,總共7種寫法):

1、餓漢式

package com.app.singlecase;

/**
 * @ClassName: HungryMan
 * @Description 單例模式餓漢式
 * @Date:2019/9/29 11:05
 * @Author: lk
 * @see
 * @since JDK 1.8
 */
public class HungryMan {
    //一個對象
    private final  static HungryMan HUNGRY_MAN=new HungryMan();
    //私有構建公開方法
    private HungryMan(){

    }
    //公開方法 獲取這個對象
    public static HungryMan getHungryMan(){
        return HUNGRY_MAN;
    }
}

優點: 簡單,使用時沒有延遲;在類裝載時就完成實例化,天生的線程安全

缺點: 沒有懶加載,啓動較慢;如果從始至終都沒使用過這個實例,則會造成內存的浪費。

2、餓漢式的變種寫發

package com.app.singlecase;

/**
 * @ClassName: HungryManTwo
 * @Description 單例模式餓漢式的變種
 * @Date:2019/9/29 11:10
 * @Author: lk
 * @see
 * @since JDK 1.8
 */
public class HungryManTwo {
    private HungryManTwo(){

    }
    private static final HungryManTwo HUNGRY_MAN_TWO;
    static {
        HUNGRY_MAN_TWO=new HungryManTwo();
    }
    private static HungryManTwo getHungryManTwo(){
        return HUNGRY_MAN_TWO;
    }
}

優缺點同上

3、懶漢式(線程不安全)

package com.app.singlecase;

/**
 * @ClassName: Slacker
 * @Description 單例模式懶漢式
 * @Date:2019/9/29 11:13
 * @Author: lk
 * @see
 * @since JDK 1.8
 */
public class Slacker {
    private Slacker(){

    }
    private static Slacker slacker;
    public static Slacker getSlacker(){
        //slacker==null存在競態條件,可能會有多個線程同時進入if語句,產生多個實例
        if(slacker==null){
            System.out.println("第一次進入");
            slacker=new Slacker();
        }
        return slacker;
    }
}

優點: 懶加載,啓動速度快、如果從始至終都沒使用過這個實例,則不會初始化該實力,可節約資源

缺點: 多線程環境下線程不安全。if (singleton == null) 存在競態條件,可能會有多個線程同時進入 if 語句,導致產生多個實例

4、懶漢式的變種(線程安全)

package com.app.singlecase;

/**
 * @ClassName: SlackerTwo
 * @Description 單例模式懶漢式線程安全
 * @Date:2019/9/29 11:18
 * @Author: lk
 * @see
 * @since JDK 1.8
 */
public class SlackerTwo {
    private SlackerTwo(){

    }
    private static SlackerTwo slackerTwo;
    public static synchronized SlackerTwo getSlackerTwo(){
        if(slackerTwo==null){
            slackerTwo=new SlackerTwo();
        }
        return slackerTwo;
    }
}

優點: 解決了上述的安全問題
缺點: 使用synchronized關鍵字對方法進行加鎖,使得每次只有一個線程進入該方法,在多線程下,效率降低。

5、雙重死鎖

package com.app.singlecase;

/**
 * @ClassName: DoubleCheckLock
 * @Description  單例模式之雙重檢查鎖
 * @Date:2019/9/29 11:24
 * @Author: lk
 * @see
 * @since JDK 1.8
 */
public class DoubleCheckLock {
    private DoubleCheckLock(){

    }

    private static volatile DoubleCheckLock doubleCheckLock;

    public static DoubleCheckLock getDoubleCheckLock(){
        if(doubleCheckLock==null){
            synchronized (DoubleCheckLock.class){
                if(doubleCheckLock==null){
                    doubleCheckLock=new DoubleCheckLock();
                }
            }

        }
        return doubleCheckLock;
    }
}

優點:線程安全;延遲加載;效率較高。

由於 JVM 具有指令重排的特性,在多線程環境下可能出現 singleton 已經賦值但還沒初始化的情況,導致一個線程獲得還沒有初始化的實例。volatile 關鍵字的作用:

  • 保證了不同線程對這個變量進行操作時的可見性
  • 禁止進行指令重排序

6、靜態內部類

package com.app.singlecase;

/**
 * @ClassName: Inside
 * @Description 單例模式之靜態內部類
 * @Date:2019/9/29 11:29
 * @Author: lk
 * @see
 * @since JDK 1.8
 */
public class Inside {
    private Inside(){

    }
    //靜態內部類
    private static class SingletonInstance {
        private static final Inside INSIDE=new Inside();
    }

    public static Inside getInside(){
        return SingletonInstance.INSIDE;
    }
}

優點: 避免了線程不安全,延遲加載,效率高。
靜態內部類的方式利用了類裝載機制來保證線程安全,只有在第一次調用getInside方法時,纔會裝載SingletonInstance內部類,完成Singleton的實例化,所以也有懶加載的效果。

7、枚舉(最簡單且線程安全外加能防止序列號攻擊)

package com.app.singlecase;

/**
 * @ClassName: SingletonEnum
 * @Description 單例模式之枚舉模式
 * @Date:2019/9/29 11:32
 * @Author: lk
 * @see
 * @since JDK 1.8
 */
public enum SingletonEnum {
    SINGLETON_ENUM;
    public void getEnumTest(){
        System.out.println("枚舉測試");
    }
}

優點: 通過JDK1.5中添加的枚舉來實現單例模式,寫法簡單,且不僅能避免多線程同步問題,而且還能防止反序列化重新創建新的對象。

作者也是看的另外一片博客學習的,下面是那個博客的鏈接
作者學習的博客地址

祝大家面試順利,一起加油。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章