單例模式--java代碼實現


單例模式

  單例模式,顧名思義,在程序運行中,實例化某個類時只實例化一次,即只有一個實例對象存在。例如在古代,一個國家只能有一個皇帝,在現代則是主席或總統等。

  在Java語言中單例模式有以下實現方式

1.餓漢式

import org.junit.jupiter.api.Test;

public class Singleton {
    //靜態成員變量
    private static Singleton singleton = new Singleton();
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    //構造函數私有化
    private Singleton(){
    }
    //返回靜態資源中Singleton實例
    public static Singleton getInstance() {
        return singleton;
    }
    /**
     * 單元測試
     */
    @Test
    public void testSingleton() {
        //通過調用類的靜態方法返回類的實例對象
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        s1.setName("zhangsan");
        s2.setName("lisi");
        System.out.println(s1.getName());
        System.out.println(s2.getName());
    }
}

  在類加載時,直接將實例對象初始化,並且該實例是靜態的,屬於類的成員變量,通過調用類的靜態方法返回該對象。  

  運行testSingleton單元測試,輸出的兩行都是lisi,因為s1,s2指向的同一個實例對象,這個對象在類創建的時候就存在了。

  餓漢式是線程安全的,不管系統的那一個線程獲取這個對象,他們都是該類同一個對象。缺點是在程序在一開始就創建了該對象,占用內存空間。下面這種實現方式增加判斷,在程序調用時才實例化該對象。

2.懶漢式

import org.junit.jupiter.api.Test;

public class Singleton {
    //不初始化實例對象
    private static  Singleton singleton = null;
    private String name;

    public String getName() {
        return name;
    }
     //構造函數私有化
    private Singleton(){
    }
    public void setName(String name) {
        this.name = name;
    }
    //當被調用時才動態實例化
    public static  Singleton getInstance() {
        if(singleton == null) {    
            singleton = new Singleton();
        }
        return singleton;
    }
}

  懶漢式解決了餓漢式的實例對象初始化占用內存的情況,但是懶漢式存在線程安全的問題,當多個線程同時訪問getInstance()方法時,有可能在第一個線程進入if語句是還沒new Singleton()時,這時第二個線程判斷if的時候就會為真,則會創建新的實例,單例模式設計失敗。

  這時需要在getInstance()方法上添加synchronized修飾符,表示線程同步,即當一個線程訪問該方法時,其他線程需要等待其釋放資源。

public static synchronized Singleton getInstance() {
        if(singleton == null) {    
            singleton = new Singleton();
        }
        return singleton;
    }

  這樣解決了懶漢式的線程不安全的問題,但是這樣會降低系統的性能,當一個線程占用了該資源,其他的線程只能等待,系統的性能大大下降。於是乎第三種實現模式,對懶漢式進行了改進。

3.雙重檢測

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

  雙重檢測,線程訪問時有兩次檢測。

  這里將synchronized放到判斷語句里面,這樣當第一個線程調用getInstance()方法時,singleton為空,進入synchronized代碼塊,其他的線程即使也判斷singleton為空,則需要等待第一個線程完成資源的使用,這樣保證了線程的安全。

  當實例化對象完成時,其他的線程調用getInstance()方法時,直接返回Singleton對象即可,不用再等待,這一點優化了系統,提高了系統的性能。

4.總結

  模式來源於生活。本文用Java語言實現了單例模式。三種實現方式都是線程安全的,餓漢式比較占用內存空間;懶漢式則降低了系統的性能;雙重檢測是懶漢式的升級,即能保證線程的安全,也能以懶加載的方式實現單例模式,在大規模系統中節省對象的創建時間,提高系統的性能。在系統中共享同一個資源或同一個對象,則可利用單例模式來實現。

  參考B站視頻https://www.bilibili.com/video/av34162848


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM