Java單例的寫法詳解

單例模式,顧名思義,就是全局隻保存有一個實例並且能夠避免用戶去手動實例化,所以單例模式的各種寫法都有一個共同點,不能通過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(){
    }
}

以上幾種方式基本就夠用瞭,但都有一個共同的缺點,面對序列化和反序列化,是無法保證單例的,但枚舉的特性卻能保證這一點

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: