1、定義
確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例
2、使用場景
3、五種常見的單例模式
1)餓漢單例模式(推薦)
package com.baofeng.singleton;
/**
* 餓漢單例模式
*
* @author sus
*
* 優點:加載類的時候就創建實例,所以線程安全
* 缺點:不能延遲加載
* 典型的空間換時間
*
*/
public class Singleton {
private static final Singleton singleton = new Singleton();
// 構造函數私有化
private Singleton() {
}
public void doSomething(){
System.out.println("do something");
}
public static Singleton getInstance() {
return singleton;
}
//call
//Singleton.getInstance().doSomething();
}
2)懶漢單例模式
/**
* 懶漢單例模式
*
* @author sus
*
* 優點:延遲加載
* 缺點:需要加鎖才能實現多線程同步,但是效率會降低
* 典型的時間換空間
*/
public class Singleton1 {
private static Singleton1 singleton = null;
// 構造函數私有化
private Singleton1() {
}
public void doSomething(){
System.out.println("do something");
}
public static synchronized Singleton1 getInstance() {
if(singleton == null){
singleton = new Singleton1();
}
return singleton;
}
//call
//Singleton1.getInstance().doSomething();
}
3)雙重檢查鎖定(Doublle Check Lock)單例模式(不太推薦)
/**
* 雙重檢查鎖定(Doublle Check Lock)單例模式
*
* @author sus
*
* 麻煩,在當前Java內存模型中不一定都管用,(DCL失效問題)某些平臺和編譯器甚至是錯誤的,
* 因爲singleton = new Singleton2()這種代碼在不同編譯器上的行爲和實現方式不可預知。
*
*/
public class Singleton2 {
//JDK是1.5或之後的版本添加volatile關鍵字
//雙重加鎖機制的實現會使用一個關鍵字volatile,
//它的意思是:被volatile修飾的變量的值,將不會被本地線程緩存,
//所有對該變量的讀寫都是直接操作共享內存,從而確保多個線程能正確的處理該變量。
private volatile static Singleton2 singleton = null;
// 構造函數私有化
private Singleton2() {
}
public void doSomething(){
System.out.println("do something");
}
public static Singleton2 getInstance() {
//第一層判空是爲了避免不必要的同步
if (singleton == null) {
synchronized (Singleton2.class) {
//第二層判空是爲了在null的情況下創建實例
if (singleton == null) {
singleton = new Singleton2();
}
}
}
return singleton;
}
//call
//Singleton2.getInstance().doSomething();
}
4)靜態內部類單例模式(推薦)
/**
* 靜態內部類單例模式
*
* @author sushuai
*
*延遲加載,減少內存開銷。因爲用到的時候才加載,避免了靜態field
*在單例類加載時即進入到堆內存的permanent代而永遠得不到回收的缺點(大多數垃圾回收算法是這樣)。
*
*類級的內部類,也就是靜態類的成員式內部類,該內部類的實例與外部類的實例
* 沒有綁定關係,而且只有被調用時纔會裝載,從而實現了延遲加載
*/
public class Singleton3 {
// 構造函數私有化
private Singleton3() {
}
public static Singleton3 getInstance() {
return SingletonHolder.singleton;
}
public void doSomething(){
System.out.println("do something");
}
private static class SingletonHolder {
/**
* 靜態初始化器,由JVM來保證線程安全
*/
private static final Singleton3 singleton = new Singleton3();
}
//call
//Singleton3.getInstance().doSomething();
}
5)包含單個元素的枚舉類型的單例模式(極力推薦)
/**
* 包含單個元素的枚舉類型的單例模式
*
* @author sus
*
* 很好,不僅能避免多線程同步問題,而且還能防止反序列化重新創建新的對象。
* 但是失去了類的一些特性,沒有延遲加載,用的人也太少了~~
*/
public enum Singleton4 {
INSTANCE; //定義一個枚舉的元素,就代表Singleton4的一個實例
public void doSomething(){
System.out.println("do something");
}
//call
//Singleton4.INSTANCE.doSomething();
//單元素的枚舉類型已經成爲實現Singleton的最佳方法
}