設計模式(1)_單例模式
設計模式經典的常用的就23種,單例模式算是 最好容易理解的設計模式之一了。但是要寫出高效、安全、真正意義上的單例模式並不容易:
單例模式有兩種實現方法:延遲實例化 和急切實例化
第一種:延遲實例化
顧名思義,延遲實例化,就是在使用時,纔去實例化對象;
package com.test;
/**
* 設計模式之 單例模式,
* 就是要保證一個類有且只能存在一個實例對象
* @author crg
*
*/
public class Singleton {
private static Singleton uniqueInstance;
/**
* 構造方法私有化,外部不能訪問
*/
private Singleton(){}
/**
* 向外部提供獲取該類實例的靜態方法
* @return
*/
public static Singleton getInstance(){
if (uniqueInstance == null) {
//該類對象不存在 才創建實例對象,如果存在直接返回該
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
一般都是這麼寫的。但是這真的是單例嗎?下面測試一下:
測試代碼:
package com.test;
public class MainTest {
/**
* @param args
*/
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 獲得實例對象
Singleton mSingleton = Singleton.getInstance();
//打印出對象的內存地址
System.out.println(mSingleton);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
Singleton mSingleton = Singleton.getInstance();
System.out.println(mSingleton);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
Singleton mSingleton = Singleton.getInstance();
System.out.println(mSingleton);
}
}).start();
}
}
測試結果:
com.test.Singleton@3ce53108
com.test.Singleton@6af62373
com.test.Singleton@6af62373
com.test.Singleton@2e6e1408
com.test.Singleton@3ce53108
com.test.Singleton@3ce53108
很明顯這不是單例模式,因爲同時存在兩個不同的對象實例。原因是:這種寫法在多線程環境下是不安全的,可能會同時多個線程同時訪問 getInstance() 方法,就像剛纔的測試結果。
通過 synchronized 關鍵字,同步 getInstance() 該方法,可以解決該問題,使線程安全,即可以實現單例模式。
/**
* 向外部提供獲取該類實例的靜態方法
* @return
*/
public static synchronized Singleton getInstance(){
if (uniqueInstance == null) {
//該類對象不存在 才創建實例對象,如果存在直接返回該
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
結果如下:
com.test.Singleton@6af62373
com.test.Singleton@6af62373
com.test.Singleton@6af62373
但是 這樣做,雖然保證了安全,但是使性能降低,同步極大的影響性能。關鍵是隻有當 uniqueInstance = new Singleton() 第一次執行時,才需要同步。
解決辦法如下,使用雙重檢查加鎖同步代碼塊:
/**
* 延遲實例化 在多線程環境下,爲了安全性,需要同步,降低了性能,
* 以空間換時間
*/
/**
* volatile 關鍵字保證 uniqueInstance 線程之間的可見性
*/
private volatile static Singleton uniqueInstance;
/**
* 構造方法私有化,外部不能訪問
*/
private Singleton(){}
/**
* 向外部提供獲取該類實例的靜態方法
* @return
*/
public static Singleton getInstance(){
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
//該類對象不存在 才創建實例對象,如果存在直接返回該
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
二,急切實例化
private static Singleton uniqueInstance = new Singleton();
/**
* 急切實例化 隨着類的加載 jvm 已經創建了 uniqueInstance
* 保證了安全性,也提高了性能
* 但是,佔用空間,即便在整個生命週期中,一此不使用 uniqueInstance,uniqueInstance 對象也會一直在內存中
* 是以空間換時間的解決辦法
* 構造方法私有化,外部不能訪問
*/
private Singleton(){}
public static Singleton getInstance(){
return uniqueInstance;
}
JVM 在加載這個類時,馬上創建此唯一的單件實例。
三,最優方案
/**
* 時間和空間的方案
*/
/**
* 私有構造方法,禁止外部訪問
*/
private Singleton(){}
/**
* 靜態內部類
* @author crg
*
*/
private static class HolderClass{
private static final Singleton uniqueInstance = new Singleton();
}
public static Singleton getInstance(){
return HolderClass.uniqueInstance;
}