欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的各種單例模式優(yōu)缺點解析

 更新時間:2023年07月06日 08:47:22   作者:索碼理  
這篇文章主要介紹了Java中的各種單例模式解析,單例模式是Java中最簡單的設(shè)計模式之一,這種類型的設(shè)計模式屬于創(chuàng)建者模式,它提供了一種訪問對象的最佳方式,需要的朋友可以參考下

單例模式

單例模式是一種創(chuàng)建型設(shè)計模式,其目的是確保類只有一個實例,并且提供全局訪問點以訪問該實例。

在 Java 中,實現(xiàn)單例模式有多種方式,下面介紹其中的一些。

餓漢式單例模式

在餓漢式單例模式中,實例在類加載時就被創(chuàng)建,因此可以保證實例的唯一性。該模式的實現(xiàn)非常簡單,可以使用靜態(tài)初始化器或者私有構(gòu)造方法來實現(xiàn)。例如:

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

在這個例子中, INSTANCE 是一個靜態(tài)常量,它在類加載時被初始化為 Singleton 類的實例。getInstance() 方法提供了對該實例的全局訪問點。

優(yōu)點:

  • 線程安全,因為實例在類加載的時候就已經(jīng)創(chuàng)建好了。
  • 簡單易用,沒有線程同步等復(fù)雜問題。
  • 線程訪問單例實例的速度比懶漢式單例模式更快。

缺點:

  • 類加載時就創(chuàng)建實例,有可能會浪費資源。
  • 如果單例類依賴于其他類,這些依賴的類在類加載時也會被創(chuàng)建,

懶漢式單例模式

在懶漢式單例模式中,實例在第一次使用時才被創(chuàng)建。

public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance(){
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

在上面的實現(xiàn)中,instance 是靜態(tài)變量,用于存儲單例對象。在 getInstance 方法中,如果 instance 為 null,則創(chuàng)建一個新的 Singleton 對象,否則直接返回 instance。由于沒有進行同步鎖定,所以線程不安全,可能會導(dǎo)致并發(fā)創(chuàng)建多個實例的問題。

優(yōu)點:

  • 延遲對象的創(chuàng)建時間,減少內(nèi)存占用。
  • 簡單易懂,易于實現(xiàn)。

缺點:

  • 非線程安全,需要考慮并發(fā)情況。
  • 多線程環(huán)境下,需要使用同步鎖或者雙重校驗鎖來保證線程安全,可能會影響性能。

雙重校驗鎖單例模式

“雙重檢查鎖”(Double-Checked Locking) 是懶漢式的一種優(yōu)化,這種實現(xiàn)方式結(jié)合了懶漢式和餓漢式的優(yōu)點,既能夠延遲對象的創(chuàng)建時間,又能夠保證線程安全。

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

在這個例子中, INSTANCE 是一個 volatile 的靜態(tài)變量,它在第一次使用時被創(chuàng)建。getInstance() 方法使用雙重檢查鎖定來確保 INSTANCE 的唯一性。當(dāng)多個線程同時訪問 getInstance() 方法時,只有第一個線程會獲得鎖定并創(chuàng)建實例。其他線程會等待鎖定被釋放后再次檢查 INSTANCE 是否為空,從而避免了多次創(chuàng)建實例的情況。

需要注意的是,使用 volatile 關(guān)鍵字可以確保 INSTANCE 變量的可見性和有序性,從而保證多線程環(huán)境下的正確性。

優(yōu)點:

  • 延遲加載,只有當(dāng)需要用到實例時才會創(chuàng)建,節(jié)省了系統(tǒng)資源。
  • 線程安全,適合高并發(fā)場景。

缺點:

  • 實現(xiàn)復(fù)雜,容易出現(xiàn)問題。
  • 由于 Java 內(nèi)存模型的限制,可能會出現(xiàn)指令重排的問題,需要使用 volatile 關(guān)鍵字來解決。

枚舉單例模式

枚舉單例模式是一種比較新的單例模式實現(xiàn)方式,它在 Java 5 中引入。它利用了 Java 中枚舉類型的特性來實現(xiàn)單例模式,枚舉類型的每個枚舉常量都是單例的。

public enum Singleton {
    INSTANCE;
    public void doSomething() {
        // do something...
    }
}

在這個例子中,枚舉類型 Singleton 只有一個枚舉常量 INSTANCE ,該常量在枚舉類型初始化時被創(chuàng)建。由于枚舉類型的實例在 Java 虛擬機中是唯一的,因此枚舉常量也是單例的。我們可以通過 Singleton.INSTANCE 來訪問該實例,并調(diào)用 doSomething() 方法。

優(yōu)點:

  • 簡單明了: 枚舉單例模式的實現(xiàn)非常簡單,而且可以保證線程安全和實例的唯一性。
  • 序列化安全: 由于枚舉實例在 JVM 中是唯一的,因此可以保證序列化和反序列化的安全性。
  • 防止反射攻擊: 枚舉實例在 JVM 中是唯一的,因此可以避免反射攻擊。

缺點:

  • 不支持延遲加載 ,因為枚舉類型的實例在類加載時就已經(jīng)被創(chuàng)建了。
  • 不能繼承其他類 ,因為枚舉類型默認繼承了Enum類。

靜態(tài)內(nèi)部類單例模式

靜態(tài)內(nèi)部類單例模式是一種優(yōu)雅而簡潔的實現(xiàn)方式。它利用了 Java 類加載器的機制來保證實例的唯一性,并避免了餓漢式單例模式的缺點。

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

在這個例子中, Singleton 類的構(gòu)造方法是私有的,只能在 Singleton 類的內(nèi)部進行調(diào)用。 SingletonHolder 類是 Singleton 類的一個靜態(tài)內(nèi)部類,它在 Singleton 類被加載時并不會立即被加載,而是在第一次調(diào)用 Singleton.getInstance() 方法時才會被加載,從而實現(xiàn)了延遲加載。

由于靜態(tài)內(nèi)部類 SingletonHolder 只會被加載一次,因此 INSTANCE 實例也只會被創(chuàng)建一次,從而保證了實例的唯一性。總的來說,靜態(tài)內(nèi)部類單例模式是一種比較優(yōu)秀的單例模式實現(xiàn)方式,它兼顧了線程安全、懶加載和高效等特點,是一種值得推薦的單例模式實現(xiàn)方式。

優(yōu)點:

  • 線程安全: 靜態(tài)內(nèi)部類只會被加載一次,因此可以保證單例對象的線程安全性;
  • 懶加載:靜態(tài)內(nèi)部類只有在被調(diào)用時才會被加載,從而實現(xiàn)了懶加載的效果;
  • 高效:靜態(tài)內(nèi)部類單例模式?jīng)]有加鎖,所以性能比懶漢式和餓漢式都要高;
  • 簡單: 靜態(tài)內(nèi)部類單例模式的實現(xiàn)比較簡單,代碼量較少。

缺點:

  • 不易理解: 相比于餓漢式和懶漢式,靜態(tài)內(nèi)部類單例模式的實現(xiàn)方式可能不太容易理解;
  • 無法傳遞參數(shù): 靜態(tài)內(nèi)部類單例模式的構(gòu)造函數(shù)是私有的,無法傳遞參數(shù)。如果需要傳遞參數(shù),需要使用其他方式實現(xiàn)。

注冊式單例模式

注冊式單例模式是一種靈活而可擴展的實現(xiàn)方式。在該模式中,單例實例被注冊到一個全局的注冊表中,可以實現(xiàn)對象的統(tǒng)一管理和獲取,但需要注意容器的生命周期和線程安全問題。

下面是一個使用 ConcurrentHashMap 實現(xiàn)注冊式單例模式的例子:

public class Singleton {
    private static Map<String, Singleton> instances = new ConcurrentHashMap<>();
    static {
        instances.put(Singleton.class.getName(), new Singleton());
    }
    private Singleton() {}
    public static Singleton getInstance() {
        return instances.get(Singleton.class.getName());
    }
    public static void register(String key, Singleton instance) {
        instances.put(key, instance);
    }
    public static Singleton getRegisteredInstance(String key) {
        return instances.get(key);
    }
}

Spring 框架中的 Bean 注冊機制使用的是注冊式單例模式。在 Spring 中, Bean 的注冊是通過 BeanDefinitionRegistry 接口來完成的,而 BeanDefinitionRegistry 接口的實現(xiàn)類包括了 DefaultListableBeanFactoryGenericApplicationContext 等。這些實現(xiàn)類在內(nèi)部都使用了類似于注冊式單例模式的方式來注冊和管理 Bean 。

具體來說, Spring 在注冊 Bean 時,會將 Bean 的定義信息封裝成一個 BeanDefinition 對象,并將其注冊到一個全局的 BeanFactory 中,以供后續(xù)的使用。在注冊 Bean 的過程中, Spring 會根據(jù) BeanDefinition 中的配置信息來創(chuàng)建相應(yīng)的 Bean 實例,同時還會對其進行依賴注入、生命周期管理等操作。

需要注意的是,SpringBean 注冊機制中,雖然使用了類似于注冊式單例模式的方式來管理 Bean ,但它并不是一個完全的單例模式實現(xiàn)。在 Spring 中, Bean 的單例性是在運行時動態(tài)實現(xiàn)的,而不是在編譯期就確定的。也就是說,如果在 BeanDefinition 中將 scope 屬性設(shè)置為 prototype ,那么每次獲取該 Bean 實例時都會創(chuàng)建一個新的對象,而不是返回同一個單例實例。

優(yōu)點:

  • 可以管理多個單例實例,可以通過名稱或者其他方式來獲取實例。
  • 避免了全局變量帶來的問題,比如命名沖突、不同作用域訪問困難等。

缺點:

  • 容易造成內(nèi)存泄漏,因為單例實例不會被釋放。
  • 可能會造成重復(fù)創(chuàng)建對象的問題。

ThreadLocal單例模式

ThreadLocal單例模式這種實現(xiàn)方式將單例對象存儲在ThreadLocal中,每個線程都有自己的單例對象副本,保證了線程安全。

public class Singleton {
    private static final ThreadLocal<Singleton> singletonThreadLocal = 
        new ThreadLocal<Singleton>() {
            @Override
            protected Singleton initialValue() {
                return new Singleton();
            }
        };
    private Singleton() {}
    public static Singleton getInstance() {
        return singletonThreadLocal.get();
    }
}

在這個示例中,每個線程都有自己的 Singleton 對象副本,使用 ThreadLocal 可以避免線程安全問題。

優(yōu)點:

  • 每個線程都有自己的單例對象副本,線程安全。
  • 可以避免鎖競爭,提高性能。

缺點:

  • 可能會導(dǎo)致內(nèi)存泄漏問題,需要注意對象的生命周期。

CAS單例模式

CAS單例模式這種實現(xiàn)方式利用了CAS原子操作的特性,可以保證線程安全,但需要注意性能問題。

public class Singleton {
    private static volatile Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    Singleton temp = new Singleton();
                    if (compareAndSetInstance(null, temp)) {
                        instance = temp;
                    }
                }
            }
        }
        return instance;
    }
    private static boolean compareAndSetInstance(Singleton expect, Singleton update) {
        return Unsafe.getUnsafe().compareAndSwapObject(Singleton.class, 
            Unsafe.objectFieldOffset(Singleton.class, "instance"), expect, update);
    }
}

在這個示例中,使用了 volatileCAS 原子操作來保證線程安全,同時實現(xiàn)了懶加載。需要注意的是,使用 Unsafe 類需要特殊權(quán)限,并且 CAS 實現(xiàn)的復(fù)雜度比較高,適用于高并發(fā)場景。

優(yōu)點:

  • 線程安全,高并發(fā)場景性能較好。

缺點:

  • 實現(xiàn)復(fù)雜,需要理解 CAS 原子操作。
  • 需要使用 Unsafe 類,需要特殊權(quán)限。
  • 適用于高并發(fā)場景,對于低并發(fā)場景可能沒有明顯的性能優(yōu)勢。

總結(jié)

總之,單例模式是一種非常常用的設(shè)計模式,可以確保類只有一個實例,并提供全局訪問點以訪問該實例。在 Java 中,有多種方式可以實現(xiàn)單例模式,開發(fā)者可以根據(jù)實際需要選擇適合自己的實現(xiàn)方式。

到此這篇關(guān)于Java中的各種單例模式優(yōu)缺點解析的文章就介紹到這了,更多相關(guān)Java單例模式解析內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論