單例模式-懶漢式的四種方案


餓漢式

餓漢式的代碼很簡單也不是我們的重點。

package singleton_k;/*
 * @auther 頂風少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-01-15 20:41
 * @notify
 * @version 1.0
 */

public class Computer {

    private Computer() {
    }

    private static Computer computer = new Computer();

    public static Computer getInstance() {
        return computer;
    }
}
View Code

懶漢式-同步鎖

這種synchronized關鍵字的做法是很最簡單的,但是性能較差,對象的創建實際只有一次,剩下的其實都是獲取,但是卻要在獲取時也要同步。

package singleton_k;/*
 * @auther 頂風少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-01-15 20:41
 * @notify
 * @version 1.0
 */

public class Computer2 {

    private Computer2() {
    }

    private static Computer2 computer = null;

    public static synchronized Computer2 getInstance() {
        if (computer == null) {
            computer = new Computer2();
        }
        return computer;
    }
}
View Code

懶漢式-雙檢查鎖

synchronized轉移到方法中,假如有10條線程進入方法內,如果判斷==null,也只能有一個線程進入同步代碼塊,進入代碼塊后再次判斷是否==null第一個線程會將對象實例化。而后來的9條線程判斷!=null就不會再次創建。第二次再來10條線程,則第一個if判斷都不會通過,所以不會進入synchronized代碼塊也不會造成線程串行。大大的解決的性能問題。

package singleton_k;/*
 * @auther 頂風少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-01-15 20:41
 * @notify
 * @version 1.0
 */

public class Computer3 {

    private String name = "張三";

    private Computer3() {
    }

   
    private static  Computer3 computer = null;

    public static Computer3 getInstance() {
        //第二個線程判斷對象不為null
        if (computer == null) {
            synchronized (Computer3.class) {
                if (computer == null) {
                    computer = new Computer3();
                }
            }
        }
        return computer;
    }
}
View Code

但是這里還會有一點問題,如果在第一個線程new之后,但是沒有初始化成員變量。第二個線程在第一次判斷這時已經不是null了,它走到打印處,可能會沒有值。所以使用volatile關鍵字。這個關鍵字的作用禁止CPU使用緩存,禁止指令重排序。

package singleton_k;/*
 * @auther 頂風少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-01-15 20:41
 * @notify
 * @version 1.0
 */

public class Computer3 {

    private String name = "張三";

    private Computer3() {
    }

    /*
    volatile
    作用1 禁止被他修飾變量發生指令重排序
    作用2 禁止CPU使用緩存
    * */
    private static volatile Computer3 computer = null;

    public static Computer3 getInstance() {
        //第二個線程判斷對象不為null
        if (computer == null) {
            synchronized (Computer3.class) {
                if (computer == null) {
                    /*1 new:開辟內存空間
                     * 2 成員變量初始化
                     * 3 將內存中的地址賦值給變量
                     * 此處2和3可能指令重排序
                     * */
                    //第一個線程執行完下邊這一句new出來了。
                    computer = new Computer3();
                }
            }
        }
        //此處可能會出錯,出錯了。線程1的指令重排序是132但是2還沒執行就拿不到這個變量的值了
        System.out.println(computer.name);
        return computer;
    }
}
View Code

懶漢式-靜態內部類

通過靜態內部類的方式實現單例模式是線程安全的,同時靜態內部類不會在Singleton類加載時就加載,而是在調用getInstance()方法時才進行加載,達到了懶加載的效果。

package singleton_k;/*
 * @auther 頂風少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-01-15 20:41
 * @notify
 * @version 1.0
 */

public class Computer4 {

    private Computer4() {
    }

    /*
    * 通過內部類維護單例JVM在類加載的時候,是互斥的,所以可以保證線程安全問題
    * */
    private static class Computer4Factory{
        private static Computer4 computer4 = new Computer4();
    }

    public static Computer4 getComputer(){
        return Computer4Factory.computer4;
    }

}
View Code

懶漢式-枚舉

利用枚舉的特性,讓JVM來幫我們保證線程安全和單一實例的問題。除此之外,寫法還特別簡單。

package singleton_k;/*
 * @auther 頂風少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-01-15 20:41
 * @notify
 * @version 1.0
 */

public enum  Computer4 {
    INSTANCE;
    public void show(){
        System.out.println(".....");
    }
}
View Code


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM