單例模式就是是一個類僅可以建立一個對象,在java中實作主要有兩種方式:餓漢式和懶漢式。
先看兩種方式共有的部分,因為是單例,是以構造方法必須是私有的private,而且必須提供一個對外界開放的擷取對象的方法,該方法内部控制傳回唯一的一個對象執行個體:
public class Singleton {
//構造方法私有,阻斷外界直接建立對象的方法
private Singleton() {}
//提供一個擷取唯一對象執行個體的方法
public static Singleton getInstance(){
//...
}
}
以上是不管什麼方式的實作,都得遵循的一些規定,下面就介紹懶漢式和餓漢式:
餓漢式是單例類感覺自己很饑餓(将執行個體對象想象為吃的),不管有沒有别的類跟我要執行個體類,我都要自己先生成一個,像下面的實作:
public class Singleton {
//構造方法私有,阻斷外界直接建立對象的方法
private Singleton() {}
//内部持有一個類初始化時唯一建立的一個類執行個體對象
private static Singleton singleton = new Singleton();
//提供一個擷取唯一對象執行個體的方法
public static Singleton getInstance(){
return singleton;
}
}
餓漢式的有點在于編碼邏輯簡單好了解,無線程安全問題;缺點嘛,自然就是當我僅僅是想用Singleton類其他方法,他還是建立了一個對我現在來說沒用的執行個體對象,當系統中這種餓漢式的單例類多起來的時候,無疑是一種資源浪費,這個問題正好懶漢式可以解決。
懶漢式,就是自己首先持有一個空的單例類的執行個體,但是不會類一加載就建立執行個體,隻有當别人第一次要我的執行個體對象我才給他建立,懶嘛,要是沒人要我就不建立了:
public class Singleton {
//構造方法私有,阻斷外界直接建立對象的方法
private Singleton() {}
//内部持有一個單例類的引用
private static Singleton singleton = null;
//提供一個擷取唯一對象執行個體的方法
public static Singleton getInstance(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
餓漢式的有點不用說了,就是解決了懶漢式的缺點;但是他的缺點就是多線程環境下,不盡人意啊,比如兩個線程同時都是第一次去擷取Singleton類的執行個體的時候,又同時執行到if(singleton==null)這一行,兩個線程就會同時進入if語句執行體中去,可能先後執行了singleton = new Singleton();這就違反了單例模式的概念,是以還得想個辦法解決這個問題。
線程安全的餓漢式:(其實隻要将if語句上加上線程鎖,就可以避免兩個線程一起跑到這個地方來了)
public class Singleton {
//構造方法私有,阻斷外界直接建立對象的方法
private Singleton() {}
//内部持有一個單例類的引用
private static Singleton singleton = null;
//提供一個擷取唯一對象執行個體的方法
public static Singleton getInstance(){
synchronized (Singleton.class) {
if(singleton==null){
singleton = new Singleton();
}
}
return singleton;
}
}
踏哒~,解決了,但是!你發現了麼?這樣的話,每次别人想擷取sinleton執行個體的時候都得等待别的線程釋放鎖,自己再加鎖,自己再釋放鎖,而這些鎖的操作又是那麼消耗時間,能不能再優化一下。想一想,是不是第一次通路完了,隻要sington對象執行個體,不為空,直接傳回就是了,沒有現成問題啊,應該這樣:
public class Singleton {
//構造方法私有,阻斷外界直接建立對象的方法
private Singleton() {}
//内部持有一個單例類的引用
private static Singleton singleton = null;
//提供一個擷取唯一對象執行個體的方法
public static Singleton getInstance(){
if(singleton==null){
synchronized (Singleton.class) {
if(singleton==null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
完美!線程安全的懶漢式,既解決了資源浪費問題,又兼顧了線程安全問題。
不過還有個更巧妙地方法,這個就涉及到内部類的隻是,當一個類加載的時候,其内部類并不加載,而是隻要第一次用到内部類的時候内部類才會加載,而且如果這個内部類是static内部類,也就是說加載了這個類一次,以後就直接擷取他就可以了,詳細參見另一篇部落格:http://www.cnblogs.com/WreckBear/p/5812942.html。
public class Singleton {
//構造方法私有,阻斷外界直接建立對象的方法
private Singleton() {}
/*
* 搭建一個内部靜态類,外部類加載的時候,内部類并不會加載,
* 隻有當内部類被通路的時候才會被加載、初始化,加載之後就會一直儲存在記憶體中
*/
public static class Get{
public static Singleton singleton = new Singleton();
}
}
在Main中擷取:
public class Main {
public static void main(String[] args) {
Singleton singleton = Singleton.Get.singleton;
}
}
這種方法顯得更加優雅一點,至此,結束!