前言
此篇文章只是本人學習 單列模式(Singleton Pattern) 使用的筆記违帆,如有雷同几于,純屬緣分!
什么是單例黎侈?
單例模式(Singleton Pattern):確保某一個類只有一個實例馆蠕,并提供該實例的全局訪問,其構(gòu)造函數(shù)私有化是尔。它是 Java 中最簡單的設(shè)計模式(Design Pattern)殉了。
設(shè)計模式(Design Pattern):前人對特定問題經(jīng)過無數(shù)次的經(jīng)驗總結(jié)之后 ,提出的能夠解決它的優(yōu)雅的方案拟枚。它不是一種技術(shù)與方法薪铜,而是一種思想!
本質(zhì):
控制實例數(shù)量
三大要點:
線程安全
延遲加載
序列化與反序列化安全
單例的幾種寫法
1.餓漢式
public class Singleton {
//上去就是干恩溅,直接實例化
private static Singleton instances = new Singleton();
//私有化構(gòu)造函數(shù)隔箍,阻止實例化對象
private Singleton() {}
//直接返回已經(jīng)實例化了的對象
public static Singleton getInstances() {
return instances;
}
public void doSomething() {
//doSomething....
}
}
餓漢式 這種實現(xiàn)單例方法是最簡單粗暴的,它在類開始加載時就初始化了(但浪費內(nèi)存)脚乡,而且在多線程中是安全的蜒滩,是典型的空間換時間。如果單例對象初始化非衬坛恚快俯艰,而且占用內(nèi)存非常小的時候,用這種方式是比較合適的锌订,可以直接在應用啟動時加載并初始化竹握。
2.懶漢式,線程不安全
public class Singleton {
private static Singleton instances = null;
private Singleton() {}
//如果發(fā)現(xiàn)沒有實例對象辆飘,就構(gòu)造一個;如果有實例對象啦辐,直接返回
public static Singleton getInstances() {
if (instances == null) {
instances = new Singleton();
}
return instances;
}
public void doSomething() {
//doSomething
}
}
該 懶漢式 就是將單例的初始化操作,延遲加載 到需要的時候才進行蜈项,這樣做在某些場合中有很大用處昧甘。比如某個單例用的次數(shù)不是很多,但是這個單例提供的功能又非常復雜战得,而且加載和初始化要消耗大量的資源,這個時候使用懶漢式就是非常不錯的選擇庸推。但它在 多線程中不安全(比如常侦,有兩個線程,一個是線程 A贬媒,一個是線程 B聋亡,它們同時調(diào)用 getInstances 方法,就可能導致并發(fā)問題)际乘,是典型的時間換空間坡倔。
3.懶漢式,線程安全
public class Singleton {
private static Singleton instances = null;
private Singleton() {}
public static Singleton getInstances() {
//加入同步鎖 synchronized
synchronized (Singleton.class) {
if (instances == null) {
instances = new Singleton();
}
return instances;
}
}
public void doSomething() {
//doSomething
}
}
這種是最常見的解決同步問題的一種方式,使用同步鎖 **synchronized (Singleton.class) **防止多線程同時進入造成 getInstances 被多次實例化罪塔。
4.懶漢式投蝉,雙重校驗鎖
public class Singleton {
// 對保存實例的變量添加 volitile 的修飾
private volatile static Singleton instances = null;
private Singleton(){}
public static Singleton getInstances(){
//先檢查實例是否存在,如果不存在才進入下面的同步塊
if(instances == null){
//同步塊征堪,線程安全的創(chuàng)建實例
synchronized (Singleton.class) {
//再次檢查實例是否存在瘩缆,如果不存在才真正的創(chuàng)建實例
if (instances == null){
instances = new Singleton();
}
}
}
return instances;
}
public void doSomething() {
//doSomething
}
}
雙重校驗鎖 指的是:并不是每次進入 getInstances 方法都需要同步,而是先不同步佃蚜,進入方法過后庸娱,先檢查實例是否存在,如果不存在才進入下面的同步塊谐算,這是第一重檢查熟尉。進入同步塊后,再次檢查實例是否存在洲脂,如果不存在斤儿,就在同步的情況下創(chuàng)建一個實例。這是第二重檢查腮考。
雙重加鎖機制的實現(xiàn)會使用一個關(guān)鍵字 volatile 雇毫,它的意思是:被 volatile 修飾的變量的值,將不會被本地線程緩存踩蔚,所有對該變量的讀寫都是直接操作共享內(nèi)存棚放,從而確保多個線程能正確的處理該變量。
5.靜態(tài)內(nèi)部類
public class Singleton {
//類級的內(nèi)部類馅闽,也就是靜態(tài)類的成員式內(nèi)部類飘蚯,該內(nèi)部類的實例與外部類的實例
//沒有綁定關(guān)系,而且只有被調(diào)用時才會裝載福也,從而實現(xiàn)了延遲加載
private static class SingletonHolder {
//靜態(tài)初始化器局骤,由JVM來保證線程安全
private static Singleton instances = new Singleton();
}
private Singleton() {}
public static Singleton getInstances() {
return SingletonHolder.instances;
}
public void doSomething() {
//doSomething
}
}
這樣實現(xiàn)出來的單例類就是線程安全的,而且使用起來非常簡潔暴凑。
6.枚舉類型單例模式
public enum Singleton{
//定義一個枚舉的元素峦甩,它就是Singleton的一個實例
instance;
public void doSomething(){
// do something ...
}
}
這種方法是根據(jù) Effective Java 書中的說法,默認枚舉實例的創(chuàng)建是 線程安全 的.(創(chuàng)建枚舉類的單例在 JVM 層面也是能保證線程安全的), 所以不需要擔心線程安全的問題现喳,所以理論上枚舉類來實現(xiàn)單例模式是最簡單的方式凯傲。
總結(jié)
以上就是 單例模式 的幾種寫法。分別是餓漢嗦篱、懶漢冰单、同步鎖、雙重校驗鎖灸促、靜態(tài)內(nèi)部類和枚舉诫欠。我們可以根據(jù)不同的場景選擇最喜歡的一種單例模式吧涵卵!
如何使用
當需要控制一個類的實例只能有一個,而且客戶只能從一個全局訪問點訪問它時荒叼,可以選用單例模式轿偎,這些功能恰好是單例模式要解決的問題
番外閱讀
參考
ANDROID設(shè)計模式之單例模式
設(shè)計模式干貨系列:(四)單例模式【學習難度:★☆☆☆☆,使用頻率:★★★★☆】
-- 默默的劃水ing--