單例模式,顧名思義,就是全域性只儲存有一個例項並且能夠避免使用者去手動例項化,所以單例模式的各種寫法都有一個共同點,不能通過new
關鍵字去建立物件,因此,如果能夠通過構造方法例項化,那麼就一定要將其宣告為私有。
餓漢式
public class PersonResource {
public static final PersonResource PERSON_RESOURCE_SINGLETON = new PersonResource();
private PersonResource(){}
public static PersonResource getInstance() {
return PERSON_RESOURCE_SINGLETON;
}
}
這種方式可以說是最安全,也最簡單的了,但卻有一個缺點,那就是無論這個例項有沒有被使用到,都會被例項化,頗有些浪費資源
懶漢式一
既然前一種方法有些浪費資源,那就換一種寫法,讓類在被呼叫的時候例項化
public class PersonResource {
private static PersonResource personResourceSingleton;
private PersonResource() {
}
public static PersonResource getPersonResourceSingleton(){
if(null==personResourceSingleton){
personResourceSingleton = new PersonResource();
}
return personResourceSingleton;
}
}
這種方式能夠在需要用到該例項的時候再初始化,也能夠在單執行緒下很好的執行,但如果是多執行緒就容易出現問題了。
懶漢式二
public class PersonResource {
private static PersonResource personResourceSingleton;
private PersonResource() {
}
public static PersonResource getPersonResourceSingleton(){
if(null==personResourceSingleton){
personResourceSingleton = new PersonResource();
}
return personResourceSingleton;
}
}
多執行緒之所以會出現問題,是因為多個執行緒能夠併發執行getPersonResourceSingleton
方法,從而導致在判斷是否為空時出現問題。
既然如此,加上鎖 ,使其互斥即可。這裡又出現了一個問題,每次獲取例項的時候都需要加鎖解鎖,而當一個例項已經被產生後,再加鎖就有些多餘了;
懶漢式三(雙重檢查)
public class PersonResource {
private PersonResource(){ }
private volatile static PersonResource personResource;
public static PersonResource getInstance(){
if(personResource==null){
synchronized (PersonResource.class){
if(personResource==null){
personResource = new PersonResource();
}
}
}
return personResource;
}
}
既然例項確定產生後不再需要加鎖,那我們在獲取鎖前先判斷一次是否已經有例項存在就可以解決問題了
靜態內部類
public class PersonResource {
private PersonResource(){}
private static class PersonResourceHolder{
public static PersonResource personResourceSingleton = new PersonResource();
}
public static PersonResource getInstance(){
return PersonResourceHolder.personResourceSingleton;
}
}
除了雙重檢查能夠保證安全的單例外,用一個靜態內部類去持有單例也是可以的,靜態內部類保證了不會隨外部類的載入而載入,這保證了延遲載入,同時在載入該類的時候就例項化單例,保證了執行緒安全;
列舉
public enum PersonResource {
/**
* PersonResource單例
*/
personResource;
public void setPersonResource(){
}
}
以上幾種方式基本就夠用了,但都有一個共同的缺點,面對序列化和反序列化,是無法保證單例的,但列舉的特性卻能保證這一點