Java單例模式詳解
設計模式是一種思想,适合于任何一門面向對象的語言。共有23種設計模式。
單例設計模式所解決的問題就是:保證類的對象在記憶體中唯一。
舉例:
A、B類都想要操作配置檔案資訊Config.java,是以在方法中都使用了Config con=new Config();但是這是兩個不同的對象。對兩者的操作互不影響,不符合條件。
解決思路:
1.不允許其他程式使用new建立該類對象。(别人new不可控)
2.在該類中建立一個本類執行個體。
3.對外提供一個方法讓其他程式可以擷取該對象。
解決方法:單例模式。
步驟:
1.私有化該類的構造函數
2.通過new在本類中建立一個本類對象。
3.定義一個共有的方法将建立的對象傳回
單例模式分為餓漢式和懶漢式
-
餓漢式: 不支援并發
/**
* 餓漢式: 缺點:
* 此模式隻能運作在單線程下,且類在加載時就已經建立好了執行個體,不管需不需要用。
* 不适合并發程式設計,由于無論是否用到類,對象一直被建立,耗費資源
*/
public class Single {
private static final Single instance = new Single();
private Single(){};
public static Single getInstance() {
return instance;
}
}
-
懶漢式:
/**
* 懶漢式: 此模式隻能運作在單線程下,在調用擷取執行個體的方法時才建立執行個體。
*
* 多線程的缺點: 如果有兩個線程對該對象進行并發操作,當A線程要建立執行個體時,B線程早就建立完執行個體
* 導緻建立了多個執行個體的建立,進而違背了單例模式
*/
public class Single2 {
private static Single2 instance = null;
private Single2(){}
public static Single2 getInstance() {
if (instance == null)
instance = new Single2();
return instance;
}
}
-
懶漢式、支援并發、synchronized:
/**
* 懶漢式、支援多并發、效率低
* 支援多線程并發,雖然避免了單例模式的風險,但是每個程序都會在使用對象時使用方法
* 并且進行申請鎖(無論對象是否建立,每個線程都會申請判斷),導緻資源的浪費,以及時間,
* 畢竟鎖資源開銷比較大
*
* synchronized 鎖住了整個方法,當有多個線程需要通路方法時,不管執行個體有沒有建立,
* 都會要排隊等待才能拿到執行個體,效率低。需要改進:隻有第一次建立執行個體時才需要鎖,其他時候不需要加鎖。
*/
public class Single3 {
private static Single3 instance = null;
private Single3(){}
public synchronized static Single3 getInstance() {
if (instance == null){
instance = new Single3();
}
return instance;
}
}
- 雙重檢查鎖
/** * volatile 關鍵字保證了記憶體可見性,所有線程都會去主存中取資料而不是線上程的緩存中取,保證了資料的更新能實時地對任何線程可見。 * 假如有兩個線程同時到達了1,它們都去建立執行個體,這時候如果沒有第二次判斷,就會多次建立執行個體了。二次判斷保證了多線程下隻建立一個執行個體。 */ public class Single4 { private volatile static Single4 instance = null; private Single4(){} public static Single4 getInstance() { if (instance == null){ synchronized (Single4.class) { if (instance == null) instance = new Single4(); } } return instance; } } volatile作為java中的關鍵詞之一,用以聲明變量的值可能随時會别的線程修改,使用volatile修飾的變量會強制将修改的值立即寫入主存,主存中值的更新會使緩存中的值失效(非volatile變量不具備這樣的特性,非volatile變量的值會被緩存,線程A更新了這個值,線程B讀取這個變量的值時可能讀到的并不是是線程A更新後的值)。volatile會禁止指令重排。 2.volatile特性 volatile具有可見性、有序性,不具備原子性。 注意,volatile不具備原子性,這是volatile與java中的synchronized、java.util.concurrent.locks.Lock最大的功能差異,這一點在面試中也是非常容易問到的點。
- 靜态私有内部類(常用)
/**
* 内部類的好處:内部類在被調用的時候才執行個體化其靜态成員變量,高!
*/
public class Single5 {
private Single5(){}
private static class SingletonHolder{
private static Single5 instance = new Single5();
}
public static Single5 getInstance(){
return SingletonHolder.instance;
}
}