對一些重要資源的訪問, 有時需要實例創建只有一份, 那麼實例創建就需要用到單例模式 .
惡漢式:
在類初始化時候就加載了對象, 可以不用考慮多線程問題 .
缺點 : 如果對象創建比較耗資源, 提前加載會耗費性能 .
/**
* 惡漢式
* @author 張延
* */
public class SingleInstance {
// 類初始化就加載這個對象, JVM初始化是線程安全的
private static SingleInstance singleInstance = new SingleInstance();
// 私有化構造器
private SingleInstance(){};
// 提供一個公共的獲取對象方法
public static SingleInstance getInstance() {
return singleInstance;
}
}
懶漢式:
- 不加鎖
多線程情況會出現問題, 可能會導致兩份實例創建
/**
* 懶漢式
* @author 張延
* */
public class SingleInstance {
private static SingleInstance singleInstance = null;
private SingleInstance(){};
public static SingleInstance getInstance() {
if(singleInstance == null) {
singleInstance = new SingleInstance();
}
return singleInstance;
}
}
- 加鎖方式
針對上面不安全方式改進, 但是這種synchronized修飾方法會導致性能降低, 因爲getInstance是一個頻繁被使用方法,
/**
* 加鎖的懶漢式
* @author 張延
* */
public class SingleInstance1 {
private static SingleInstance1 singleInstance = null;
private SingleInstance1() {};
// 在方法上加鎖,限制多個線程同時進入,導致實例化不是一份
public synchronized static SingleInstance1 getInstance() {
if(singleInstance == null) {
singleInstance = new SingleInstance1();
}
return singleInstance;
}
}
- 雙重鎖檢查
將鎖的粒度降低, 多線程都可以進去判斷”null == singleInstance” , 只有當沒有實例創建纔會進入同步快, 再次檢查是否創建, 如果沒有創建,纔會創建這個實例 .
/**
* 雙重鎖檢查的懶漢式
* @author 張延
*
*/
public class SingleInstance2 {
private volatile static SingleInstance2 singleInstance = null;
private SingleInstance2() {};
public static SingleInstance2 getInstance() {
// 多線程可以進來判斷是否爲null,只有當沒有創建實例,纔會進入同步代碼快
if(singleInstance == null) {
// 將鎖的粒度降低,
synchronized (SingleInstance2.class) {
if(singleInstance == null) {
singleInstance = new SingleInstance2();
}
}
}
return singleInstance;
}
}
靜態內部類方式:
基本具備很多優點 , 高校調用 , 延時加載 , 線程安全
SingleInstance類被加載, 但是instance不會被初始化, 可以做到延時加載, 只有當主動獲取實例變量, 纔會顯示加載類, 因爲JVM是天然線程安全的, 不會加載兩份, 所以實例化instance很耗資源, 應該讓他延時加載 .
public class SingleInstance {
// 初始化SingleInstance的時候,內部類InnerClass不會被加載,只有在被主動使用纔會加載
private static class InnerClass {
private static SingleInstance singleInstance = new SingleInstance();
}
private SingleInstance() {};
public static SingleInstance getInstance() {
// 當這裏主動使用, 纔會加載InnerClass,纔會創建SingleInstance實例
return InnerClass.singleInstance;
}
}
總結:
餓漢式 : 線程安全 , 調用效率高 , 但是不能延時加載
懶漢式(加鎖) : 線程安全 , 調用效率不高 , 但是可以延時加載
靜態內部類 : 線程安全 , 調用效率高 , 並且還可以延時加載