面試題—5種單例模式寫法以及單線程和多線程下的區別

閒來無事看之前的博客,發現單例模式只會寫2中。所以再重新開一篇博客,將目前自己所能理解的幾種單例模式全部總結下。

___________________________________________________________________________________________________________

 

1、懶漢式(最基本的) 單線程版

寫單例模式(餓漢式)的步驟:

      1):必須在該類中,自己先創建出一個對象。

      2):私有化自身的構造器,防止外界通過構造器創建新的對象。

      3):想外暴露一個公共的靜態方法用於獲取自身的對象

缺點:單線程是沒問題的 但是多線程就會產生線程問題  下面會介紹多線程版本

//  懶漢式類初始化的,不會創建該對象,真正需要的時候纔會加載(創建),天生線程不安全,需要解決線程安全問題,所有效率比較低
public class SingletonLazy {

    private SingletonLazy() {
        System.out.println(Thread.currentThread().getName());
    }

    private static SingletonLazy singletonLazy;

    public static SingletonLazy getInstance() {
        if (singletonLazy == null) {
            singletonLazy = new SingletonLazy();
        }
        return singletonLazy;
    }

    public static void main(String[] args) {

        SingletonLazy singletonLazy1 = SingletonLazy.getInstance();
        SingletonLazy singletonLazy2 = SingletonLazy.getInstance();
        System.out.println(singletonLazy1 == singletonLazy2);   // true
    }
}

多線程版本線程不安全(最簡單的案例):

public class SingletonLazy {

    private SingletonLazy() {
        System.out.println(Thread.currentThread().getName());
    }

    private static SingletonLazy singletonLazy;

    public static SingletonLazy getInstance() {
        if (singletonLazy == null) {
            singletonLazy = new SingletonLazy();
        }
        return singletonLazy;
    }

    public static void main(String[] args) {
       
        for (int i = 0; i <= 500; i++) {
            new Thread(() -> {
                try {
                    getInstance();
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }, String.valueOf(i)).start();
        }
      
       
    }
}

2、餓漢式(最基本的)

 優點:線程天生安全  類在整個生命週期中只會被加載一次,因此該單例類只會創建一個實例,也就是說,線程每次都只能也必定只可以拿到這個唯一的對象
 缺點:類加載的時候就會加載 static 對象 如果暫時用不到呢  就會佔用極大的內存
 



public class SingletonHungry {
//    會浪費內存
//    byte[] data1 = new byte[1024*1024];
//    byte[] data2 = new byte[1024*1024];

    private SingletonHungry() {
        System.out.println(Thread.currentThread().getName());
    }

    //小知識: 當使用static 修飾時 會存檔在 JVM的方法區  JVM垃圾回收機制 不會進行回收
    private static final SingletonHungry singletonDemo = new SingletonHungry();


    private static SingletonHungry getInstance() {
        return singletonDemo;
    }

    public static void main(String[] args) {
        
        SingletonHungry singletonDemo = getInstance();
        SingletonHungry singletonDemo2 = getInstance();

        System.out.println(singletonDemo == singletonDemo2);  // true
    }
}

 

3、枚舉

枚舉是一個特殊的類
內部將構造器進行私有化,因此不能通過New 的方式進行創建

有興趣可以測試下 ,我這比較簡單

public enum  EnumSingleton {
    INSTANCE;
    public EnumSingleton getInstance(){
        return INSTANCE;
    }
}

4、靜態內部類

public class SingletonStatic {
    private SingletonStatic() {}
    public static class  SingletonClassInstance{
        private static final SingletonStatic single = new SingletonStatic();
    }
    public static SingletonStatic getInstance(){
        return SingletonClassInstance.single;
    }

    public static void main(String[] args) {
        SingletonStatic singleton1 = SingletonStatic.getInstance();
        SingletonStatic singleton2 = SingletonStatic.getInstance();

        System.out.println(singleton1 ==singleton2);
    }
}

5、DCL(雙重檢驗鎖)

在  new MultiSingletonDCL();時候由於會發生指令重排序  可能會出現問題 因此加上關鍵字   volatile

// 1. 分配內存空間
// 2. 執行構造方法,初始化對象
// 3. 把這個對象指向這個空間
// 雙鎖機制的出現是爲了解決前面同步問題和性能問題

public class SingletonDCL {

    private volatile static MultiSingletonDCL multiSingletonDCL;
    private SingletonDCL() {
        System.out.println(Thread.currentThread().getName());

    }

    private static SingletonDCL getInstance() {
        if (singletonLazyDCL == null){
            synchronized (SingletonDCL.class){
                if (singletonLazyDCL == null){
                        
                    singletonLazyDCL = new SingletonDCL();
                }
            }
        }
        return singletonLazyDCL;
    }
    public static void main(String[] args) {

        SingletonDCL instance = SingletonDCL.getInstance();
        SingletonDCL instance2 = SingletonDCL.getInstance();
        System.out.println(instance == instance2);
    }
}

本文如有問題,希望大佬指正。不勝感激。

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