單例模式
針對某一個特別大的資源或者配置文件之類的,我們希望在一個應用中全局只需要一個對象就可以了,不然會造成系統資源的額外浪費,單例模式正是適合這種模式的。
一般常見的創建方式分爲懶漢式和餓漢式。
懶漢式
懶漢式,從懶字出發,就是不到使用時,不會去動它,具體使用時調用獲取對象方法時才具體創建對象。
這種方式有其有點,在使用的時候才進行初始化,可以節約系統資源,加快應用的啓動速度等等,都是有好處的。
/**
* 非線程安全版本
* @author hexin
* @version 1.0 2018/12/17
*/
public class LazySingleton {
private static LazySingleton instance = null;
/**
* 構造函數置爲私有,不希望外部調用該方法
*/
private LazySingleton() {}
/**
* 獲取單例對象,具體使用時才初始化對象,非線程安全的
*/
public static LazySingleton getInstance() {
if (instance == null)
instance = new LazySingleton();
return instance;
}
}
下面是線程安全版本,使用了雙重校驗。
/**
* 線程安全版本
* 注意需要jdk1.5以後才行,之前版本的volatile無法保證該特點
* @author hexin
* @version 1.0 2018/12/17
*/
public class MultiThreadSafeLazySingleton {
//使用volatile保證對象不會緩存在cpu cache中。
private volatile static MultiThreadSafeLazySingleton instance = null;
private MultiThreadSafeLazySingleton(){}
/**
* 此單例方法是線程安全的,相對於直接在方法上面使用synchronized關鍵字,性能好不少,
* 不會限制爲單次只有一個線程能夠獲取對象。
* 是否初始化對象採用了雙重校驗,保證初始化只有一個對象。
*/
public static MultiThreadSafeLazySingleton getInstance(){
if (instance == null) {
synchronized (MultiThreadSafeLazySingleton.class) {
if (instance == null)
instance = new MultiThreadSafeLazySingleton();
}
}
return instance;
}
}
靜態內部類
相對於使用雙重校驗的那種線程安全實現方式,還有一種更加巧妙的實現線程安全的方式——靜態內部類。
利用了java虛擬機的特點,類加載是線程安全的,而且靜態內部類相對於其外部類來說是獨立的,加載外部類不會立即加載靜態內部類,利用這些特點,懶漢式的另一種實現方式如下:
/**
* 使用靜態內部類方式實現線程安全的延遲初始化單例對象
* @author hexin
* @version 1.0 2018/12/17
*/
public class StaticInnerLazySingleton {
private StaticInnerLazySingleton(){}
//保存單例的靜態內部類
private static class SingletonHolder {
static StaticInnerLazySingleton instance = new StaticInnerLazySingleton();
}
public static StaticInnerLazySingleton getInstance() {
return SingletonHolder.instance;
}
}
餓漢式
正如其名所知,飢餓使得對象是載入該類時即會進行對象的初始化,和懶漢式是對象的。
餓漢式相對於懶漢式,簡單,不需要考慮延遲初始化需要的各種處理,但是如果資源比較大的話,佔用資源比較多,使應用啓動速度減慢等等。
/**
* 餓漢式,立即初始化單例對象,特點是簡單,不需要考慮併發造成的問題。
* @author hexin
* @version 1.0 2018/12/17
*/
public class InstantSingleton {
//立即初始化
private final static InstantSingleton instance = new InstantSingleton();
private InstantSingleton(){}
public static InstantSingleton getInstance() {
return instance;
}
}
其他方式
- 枚舉類 枚舉其實就是很好的單例例子,其每個枚舉對象就是一個個的靜態實例。