四 單例模式

單例模式

定義

單例模式是一種對象創建型模式。使用單例模式,可以保證一個類只生成唯一的實例對象。也就是說,在整個程序空間中,該類只存在一個實例對象。

意義

提供對同一種資源的共享,節省創建對象的資源,提高效率。

爲了只能創建一個類的實例,可以將類的構造函數私有化,並提供公共的接口,對類實例對象進行訪問。

單列模式有分爲懶漢式和餓漢式。

1、懶漢式

在類初始化時,便創建對象

代碼如下:

public class Person {
    private static final Person person = new Person();
    private String name;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //構造函數私有化
    private Person() {
    }

    //提供一個全局的靜態方法
    public static Person getPerson() {
        return person;
    }
}

2、惡漢式

只有在需要類實例時,才進行創建

代碼如下:

public class Person2 {
    private String name;
    private static Person2 person;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //構造函數私有化
    private Person2() {
    }

    //提供一個全局的靜態方法
    public static Person2 getPerson() {
        if(person == null) {
            person = new Person2();
        }
        return person;
    }
}

多線程中的處理

在單線程中,上面兩種方式是沒有問題的,但是在多線程中,可以看到懶漢式是沒有問題的,而惡漢式是多線程不安全的。

那麼該如何改進惡漢式在多線程中的應用呢,最簡單的情況,便是同步,代碼如下:

處理一

public class Person3 {
    private String name;
    private static Person3 person;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //構造函數私有化
    private Person3() {
    }

    //提供一個全局的靜態方法,使用同步方法
    public static synchronized Person3 getPerson() {
        if(person == null) {
            person = new Person3();
        }
        return person;
    }
}

可以看到,我們得到了想要的結果,但是仔細觀察,發現處理一中的方法,會造成資源的浪費。例如:當線程1訪問到getPerson()函數時,那麼其他的線程就會一直等待。特別是當實例已經創建成功,但是,每次進行if(person == null) 的判斷,也會進行同步,造成效率極低。

爲了改進處理一中的方法,可以考慮只對創建實例的方法進行同步,如下:

處理二

public class Person4 {
    private String name;
    private static Person4 person;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //構造函數私有化
    private Person4() {
    }

    //提供一個全局的靜態方法
    public static Person4 getPerson() {
        if(person == null) {
            synchronized (Person4.class) {
                person = new Person4();
            }

        }
        return person;
    }
}

仔細觀察,處理二中的方法是多線程不安全的。例如:線程1訪問到synchronized (Person4.class) 時,線程2也剛好訪問到此處。那麼最終的結果便是,線程1創建了一個實例,線程2也創建了一個實例。

那麼,可以進步改進,如下:

處理三

public class Person5 {
    private String name;
    private static Person5 person;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //構造函數私有化
    private Person5() {
    }

    //提供一個全局的靜態方法
    public static Person5 getPerson() {
        if(person == null) {
            synchronized (Person5.class) {
                if(person == null) {
                    person = new Person5();
                }
            }

        }
        return person;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章