1. 程式人生 > >詳解單例模式

詳解單例模式

sstream 靜態變量 -- rgs log runnable 隨著 子句 ==

關於單例模式,話不多說,即程序運行時無論New了多少次,即內存中只有一個實例對象。即對象的HasHCode一致。

單例模式的兩大類

1、餓漢模式(即加載時就創建對象)

  -1、直接實例化餓漢模式

  -2、靜態代碼塊餓漢模式(即需要加載初始化配置的時候適用)

  -3、枚舉方式

2、懶漢式(延遲加載)

  -1、單線程安全下的懶漢

  -2、多線程安全下的懶漢

  -3、靜態內部類的懶漢(安全)

  


  1、直接實例化餓漢模式

  

package com.single;

public class Singleton {
   
    private
static final Singleton SI=new Singleton(); private Singleton(){ } public static Singleton getsingleton(){ return SI; } }

  單例模式必須保證自行創建,並且內部提供一個靜態變量來保存這個唯一的對象,構造器私有化,即其他類內部中無法直接New當前的單例類,還需要提供一個對外獲取實例的方法

  當前模式在加載類的字節碼的時候當前類的實例就立即會被創建

  2、靜態代碼塊餓漢模式(即需要加載初始化配置的時候適用)

  

 1 public class Singleton2 {
 2 
 3     private static final Singleton2 SI;
 4 
 5     private String name;
 6     static {
 7         Properties properties=new Properties();
 8         try {
 9      
10             properties.load(Singleton2.class.getClassLoader().getResourceAsStream("db.properties"));
11 String name = properties.getProperty("name"); 12 SI=new Singleton2(name); 13 } catch (IOException e) { 14 throw new RuntimeException(e); 15 } 16 17 } 18 19 private Singleton2(String name){ 20 this.name=name; 21 } 22 23 public static Singleton2 getsingleton(){ 24 return SI; 25 }

 當我們需要在類創建的時候初始化參數,並且加載某些配置文件的時候可以使用 靜態代碼塊的方式,在創建實例的時候通過構造器來完成相關參數的初始化。

 3.枚舉方式的單例,自JDK1.5之後,首先枚舉類和普通類的區別是什麽?

使用enum定義的枚舉類默認繼承了java.lang.Enum類

枚舉類的構造器只能使用private

枚舉類的每個實例必須在枚舉類中顯示的列出(,分隔 ;結尾) 列出的實例系統會自動添加public static final修飾

所有的枚舉類都定義了一個values方法,該方法可以很方便的遍歷所有的枚舉值

可以在switch表達式使用枚舉類對象作為表達式,case子句可以直接使用枚舉的名字,無需添加枚舉類作為限定

枚舉類對象的屬性不能更改,所以要用private final修飾

枚舉類對象要在構造器中被賦值
---------------------
作者:weirdowang
來源:CSDN
原文:https://blog.csdn.net/weirdowang/article/details/79970673

package com.single;
/*
枚舉方式的單例
 */
public enum SingEnum {
    SING;
    SingEnum(){

    }
    public void info(){
        System.out.println("顯示");
    }
}

  是不是很簡潔呢? 確實如此,單例模式下 枚舉方式的單例是最簡潔的

  二、懶漢模式


  1、單線程安全下的懶漢

  

package com.single2;
/*
    懶漢模式
 */
public class Singleton {
    private static Singleton singleton;

    private Singleton(){
        System.out.println("創建");
    }

    public static Singleton getSingleton(){
        if (singleton==null){
            singleton=new Singleton();
        }
        return singleton;
    }


}

  我們可以看出無論怎麽樣,當前的懶漢都是在調用的時候才被加載創建的,但是它只是在單線程的情況下是安全的為什麽呢? 現在有一個需求即多個線程對這個 單例進行訪問構建對象,在構建對象的時候使當前線程短暫的休眠一下

package com.single2;
/*
    懶漢模式測試線程不安全
 */
public class Singleton2 {
    private static Singleton2 singleton;

    private Singleton2(){
        System.out.println("創建");
    }

    public static Singleton2 getSingleton() throws InterruptedException {
        if (singleton==null){
            Thread.sleep(100);
            singleton=new Singleton2();
        }
        return singleton;
    }


}

測試代碼如下:

  

  /*
    懶漢模式下的多線程不安全
     */
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                Singleton2 singleton1 = Singleton2.getSingleton();
                System.out.println(singleton1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Singleton2 singleton2 = Singleton2.getSingleton();
                    System.out.println(singleton2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        thread2.start();
    }

  經過測試,多線程情況下這種是不安全的 為什麽呢? 第一個線程進入 進去之後, 線程休眠100ms 在此期間,第二個線程也開始進去到了創建的方法,由於當前第一個線程正在休眠,所以當前單例為null ,然後第一個線程休眠結束,創建第一個對象,此後第二個線程還在if中,也相繼創建對象,此時便構造成了線程不安全的懶漢單例

  2、線程安全的懶漢單例(雙端檢索)

package com.single2;

/*
懶漢模式 雙端檢索,避免了多次線程等待
 */
public class Singleton4 {
    private static Singleton4 singleton4;

    private Singleton4() {

    }

    public static Singleton4 getSingleton4() {
        if (singleton4 == null) {
            synchronized (Singleton4.class) {
                if (singleton4 == null) {
                    try {
                        Thread.sleep(100);

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    singleton4 = new Singleton4();
                }
            }
        }
        return singleton4;
    }

}
使用synchronized 保證當前只能有一個線程進入 ,並且在外再次進行判斷 若當前的單例已經被創建過了,避免了再次加鎖,直接返回 
3、靜態內部類的懶漢單例:(線程安全的)
package com.single2;

public class Singleton5 {
    private Singleton5(){

    }

    private static class demo{
        private static final Singleton5 SINGLETON_5=new Singleton5();
    }

    public static Singleton5 singleton5(){
        return demo.SINGLETON_5;
    }


}

也是懶漢模式最簡單的單例實現,靜態內部類不會隨著外部類的初始化而初始化,並且內部類具有自己的類加載器,是安全的

如上便是單例模式的六種創建方式,歡迎大牛指正,源碼放到群裏了 另外也歡迎各位朋友們一起學習交流, qq群:956809929

 

詳解單例模式