前言
此篇文章只是本人學(xué)習(xí) 單列模式(Singleton Pattern) 使用的筆記,如有雷同,純屬緣分!
什么是單例?
單例模式(Singleton Pattern):確保某一個(gè)類只有一個(gè)實(shí)例,并提供該實(shí)例的全局訪問,其構(gòu)造函數(shù)私有化。它是 Java 中最簡(jiǎn)單的設(shè)計(jì)模式(Design Pattern)。
設(shè)計(jì)模式(Design Pattern):前人對(duì)特定問題經(jīng)過無數(shù)次的經(jīng)驗(yàn)總結(jié)之后 ,提出的能夠解決它的優(yōu)雅的方案。它不是一種技術(shù)與方法,而是一種思想!
本質(zhì):
控制實(shí)例數(shù)量
三大要點(diǎn):
線程安全
延遲加載
序列化與反序列化安全
單例的幾種寫法
1.餓漢式
public class Singleton {
//上去就是干,直接實(shí)例化
private static Singleton instances = new Singleton();
//私有化構(gòu)造函數(shù),阻止實(shí)例化對(duì)象
private Singleton() {}
//直接返回已經(jīng)實(shí)例化了的對(duì)象
public static Singleton getInstances() {
return instances;
}
public void doSomething() {
//doSomething....
}
}
餓漢式 這種實(shí)現(xiàn)單例方法是最簡(jiǎn)單粗暴的,它在類開始加載時(shí)就初始化了(但浪費(fèi)內(nèi)存),而且在多線程中是安全的,是典型的空間換時(shí)間。如果單例對(duì)象初始化非常快,而且占用內(nèi)存非常小的時(shí)候,用這種方式是比較合適的,可以直接在應(yīng)用啟動(dòng)時(shí)加載并初始化。
2.懶漢式,線程不安全
public class Singleton {
private static Singleton instances = null;
private Singleton() {}
//如果發(fā)現(xiàn)沒有實(shí)例對(duì)象,就構(gòu)造一個(gè);如果有實(shí)例對(duì)象,直接返回
public static Singleton getInstances() {
if (instances == null) {
instances = new Singleton();
}
return instances;
}
public void doSomething() {
//doSomething
}
}
該 懶漢式 就是將單例的初始化操作,延遲加載 到需要的時(shí)候才進(jìn)行,這樣做在某些場(chǎng)合中有很大用處。比如某個(gè)單例用的次數(shù)不是很多,但是這個(gè)單例提供的功能又非常復(fù)雜,而且加載和初始化要消耗大量的資源,這個(gè)時(shí)候使用懶漢式就是非常不錯(cuò)的選擇。但它在 多線程中不安全(比如,有兩個(gè)線程,一個(gè)是線程 A,一個(gè)是線程 B,它們同時(shí)調(diào)用 getInstances 方法,就可能導(dǎo)致并發(fā)問題),是典型的時(shí)間換空間。
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) **防止多線程同時(shí)進(jìn)入造成 getInstances 被多次實(shí)例化。
4.懶漢式,雙重校驗(yàn)鎖
public class Singleton {
// 對(duì)保存實(shí)例的變量添加 volitile 的修飾
private volatile static Singleton instances = null;
private Singleton(){}
public static Singleton getInstances(){
//先檢查實(shí)例是否存在,如果不存在才進(jìn)入下面的同步塊
if(instances == null){
//同步塊,線程安全的創(chuàng)建實(shí)例
synchronized (Singleton.class) {
//再次檢查實(shí)例是否存在,如果不存在才真正的創(chuàng)建實(shí)例
if (instances == null){
instances = new Singleton();
}
}
}
return instances;
}
public void doSomething() {
//doSomething
}
}
雙重校驗(yàn)鎖 指的是:并不是每次進(jìn)入 getInstances 方法都需要同步,而是先不同步,進(jìn)入方法過后,先檢查實(shí)例是否存在,如果不存在才進(jìn)入下面的同步塊,這是第一重檢查。進(jìn)入同步塊后,再次檢查實(shí)例是否存在,如果不存在,就在同步的情況下創(chuàng)建一個(gè)實(shí)例。這是第二重檢查。
雙重加鎖機(jī)制的實(shí)現(xiàn)會(huì)使用一個(gè)關(guān)鍵字 volatile ,它的意思是:被 volatile 修飾的變量的值,將不會(huì)被本地線程緩存,所有對(duì)該變量的讀寫都是直接操作共享內(nèi)存,從而確保多個(gè)線程能正確的處理該變量。
5.靜態(tài)內(nèi)部類
public class Singleton {
//類級(jí)的內(nèi)部類,也就是靜態(tài)類的成員式內(nèi)部類,該內(nèi)部類的實(shí)例與外部類的實(shí)例
//沒有綁定關(guān)系,而且只有被調(diào)用時(shí)才會(huì)裝載,從而實(shí)現(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
}
}
這樣實(shí)現(xiàn)出來的單例類就是線程安全的,而且使用起來非常簡(jiǎn)潔。
6.枚舉類型單例模式
public enum Singleton{
//定義一個(gè)枚舉的元素,它就是Singleton的一個(gè)實(shí)例
instance;
public void doSomething(){
// do something ...
}
}
這種方法是根據(jù) Effective Java 書中的說法,默認(rèn)枚舉實(shí)例的創(chuàng)建是 線程安全 的.(創(chuàng)建枚舉類的單例在 JVM 層面也是能保證線程安全的), 所以不需要擔(dān)心線程安全的問題,所以理論上枚舉類來實(shí)現(xiàn)單例模式是最簡(jiǎn)單的方式。
總結(jié)
以上就是 單例模式 的幾種寫法。分別是餓漢、懶漢、同步鎖、雙重校驗(yàn)鎖、靜態(tài)內(nèi)部類和枚舉。我們可以根據(jù)不同的場(chǎng)景選擇最喜歡的一種單例模式吧!
如何使用
當(dāng)需要控制一個(gè)類的實(shí)例只能有一個(gè),而且客戶只能從一個(gè)全局訪問點(diǎn)訪問它時(shí),可以選用單例模式,這些功能恰好是單例模式要解決的問題
番外閱讀
參考
ANDROID設(shè)計(jì)模式之單例模式
設(shè)計(jì)模式干貨系列:(四)單例模式【學(xué)習(xí)難度:★☆☆☆☆,使用頻率:★★★★☆】
-- 默默的劃水ing--