一文搞懂java單例模式

單例模式作爲java設計模式裏最常用的一種設計模式之一,在我們日常的開發中大量被使用,現在我想從理論到實踐重新梳理一下這個知識點,幫助基礎薄弱的自己進一步加深理解。

什麼是單例模式(Singleton)

“單例”,顧名思義就是隻有一個類的實例

單:唯一

例:實例

 

單例設計模式:確保某一個類只有一個實例,自行實例化並向整個系統提供這個實例,同時也提供全局訪問的方法以供調用。

 

 

要點

  1. 確保一個類只能有一個實例
  2. 必須是自行創建這個實例,避免外部創建實例或者被子類繼承從而創建額外實例
  3. 必須自行的向整個系統提供這個實例

 

從代碼實現角度來看:

  1. 將單例類的構造函數私有化,這樣外部代碼就無法直接用new來實例化該對象
  2. 定義一個該類的靜態私有對象
  3. 提供一個靜態的公共方法用於創建;獲取類的靜態私有對象

 

有多種實現方式,主要分爲以下兩大類:

  1. 餓漢式:

 

優點:直接創建對象,不存在線程安全問題,在類創建好的同時對象生成,調用獲得對象實例的方法反應速度快,代碼簡潔。

 

缺點:資源利用效率低,用不用的都創建了;系統啓動加載時間可能會比較長

 

  1. 懶漢式:

 

優點:延遲加載對象,資源利用效率高

 

缺點:代碼相對複雜一點;線程不安全,要加鎖控制,這就會導致性能受到影響;第一次加載類的對象反應稍慢

餓漢式代碼demo

package com.java_foundation.singleton;

/**
 * @program: java_foundation
 * @description:
 * 餓漢式:直接創建對象,不管是否使用該對象都會創建
 * 特點:1)構造器私有化
 *      2)自行創建,並用靜態變量保存
 *      3)向外部提供這個實例
 *      4)可以使用final修飾,強調這是一個單例
 * @author: xiongbangwen <Email>[email protected]</Email>
 * @create: 2020-05-30 20:35
 **/
public class Singleton_1 {
    public static final Singleton_1 INSTANCE = new Singleton_1();
    private Singleton_1(){
        System.out.println("單例模式");
    }
}

枚舉式單例

package com.java_foundation.singleton;

/**
 * @program: java_foundation
 * @description: 枚舉
 * 表示該類型的對象是有限的幾個
 * 當限定爲一個時,就變成了單例
 * 重寫了toString()方法,一般返回的是常量名字
 * @author: xiongbangwen <Email>[email protected]</Email>
 * @create: 2020-05-30 20:43
 **/
public enum Singleton_2 {
    INSTANCE;
}

懶漢式demo(線程不安全)

package com.java_foundation.singleton;

import java.io.IOException;
import java.util.Properties;

/**
 * @program: java_foundation
 * @description: 靜態代碼塊的單例
 * @author: xiongbangwen <Email>[email protected]</Email>
 * @create: 2020-05-30 20:52
 **/
public class Singleton_3 {

    public static final Singleton_3 INSTANCE;
    private String info;

    static {
        try {
            Properties pro = new Properties();

            pro.load(Singleton_3.class.getClassLoader().getResourceAsStream("singleton.properties"));

            INSTANCE = new Singleton_3(pro.getProperty("info"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    private Singleton_3(String info){
        this.info = info;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "Singleton_3{" +
                "info='" + info + '\'' +
                '}';
    }
}

懶漢式demo(加鎖版)

package com.java_foundation.singleton;

/**
 * @program: java_foundation
 * @description: 懶漢式:延遲創建這個實例對象,創建一次就不再創建,有線程安全問題
 * 1)構造器私有化
 * 2)用一個靜態變量保存這個唯一的實例
 * 3)提供一個靜態方法去獲取這個實例對象
 * @author: xiongbangwen <Email>[email protected]</Email>
 * @create: 2020-05-30 21:25
 **/
public class Singleton_5 {
    private static Singleton_5 instance;
    private Singleton_5(){

    }
    public static Singleton_5 getInstance(){
        if(instance == null) {
            synchronized (Singleton_5.class) {
                if (instance == null) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    instance = new Singleton_5();
                }
            }
        }
        return instance;
    }
}

懶漢式優化,用靜態內部類實現

package com.java_foundation.singleton;

/**
 * @program: java_foundation
 * @description: 在內部類被加載時和初始化,才創建INSTANCE對象
 * 靜態內部類不會自動隨着外部類的加載和初始化而初始化,它是要單獨去加載和初始化的
 * 因爲是在內部類加載和初始化時創建的,因此線程是安全的
 * @author: xiongbangwen <Email>[email protected]</Email>
 * @create: 2020-05-30 21:42
 **/
public class Singleton_6 {
    private Singleton_6(){

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

Test測試類

package com.java_foundation.singleton;

import java.util.concurrent.*;

/**
 * @program: java_foundation
 * @description: 測試
 * @author: xiongbangwen <Email>[email protected]</Email>
 * @create: 2020-05-30 20:41
 **/
public class Test {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
//        Singleton_1 s1 = Singleton_1.INSTANCE;
//        System.out.println(s1);
//        Singleton_2 s2 = Singleton_2.INSTANCE;
//        System.out.println(s2);
//        Singleton_3 s3 = Singleton_3.INSTANCE;
//        System.out.println(s3);
//        Singleton_4 s4 = Singleton_4.getInstance();
//        Singleton_4 s5 = Singleton_4.getInstance();
//        System.out.println(s4);
//        System.out.println(s5);
        //懶漢式單線程沒有問題
//        Callable<Singleton_4> c = new Callable<Singleton_4>() {
//            @Override
//            public Singleton_4 call() throws Exception {
//                return Singleton_4.getInstance();
//            }
//        };
//        ExecutorService es = Executors.newFixedThreadPool(2);
//        Future<Singleton_4> f1 = es.submit(c);
//        Future<Singleton_4> f2 = es.submit(c);
//        Singleton_4 s6 = f1.get();
//        Singleton_4 s7 = f2.get();
//        System.out.println(s6);
//        System.out.println(s7);
//        System.out.println(s6==s7);
        //加鎖
//        Callable<Singleton_5> c = new Callable<Singleton_5>() {
//            @Override
//            public Singleton_5 call() throws Exception {
//                return Singleton_5.getInstance();
//            }
//        };
//        ExecutorService es = Executors.newFixedThreadPool(2);
//        Future<Singleton_5> f1 = es.submit(c);
//        Future<Singleton_5> f2 = es.submit(c);
//        Singleton_5 s6 = f1.get();
//        Singleton_5 s7 = f2.get();
//        System.out.println(s6);
//        System.out.println(s7);
//        System.out.println(s6==s7);
        //靜態內部類形式
        Callable<Singleton_6> c = new Callable<Singleton_6>() {
            @Override
            public Singleton_6 call() throws Exception {
                return Singleton_6.getInstance();
            }
        };
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<Singleton_6> f1 = es.submit(c);
        Future<Singleton_6> f2 = es.submit(c);
        Singleton_6 s6 = f1.get();
        Singleton_6 s7 = f2.get();
        System.out.println(s6);
        System.out.println(s7);
        System.out.println(s6==s7);
    }
}

 

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