java設計模式-單例模式

單例模式

 概述

  單例模式是 Java 比較簡單,也是最基礎,最經常使用的設計模式之一。在運行期間,保證某個類只建立一個實例,保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。設計模式

  單例模式主要有餓漢式單例懶漢式單例靜態內部類實現單例枚舉類實現單例等,不一樣的方式有不一樣的優缺點,下面介紹各個實現方式和優缺點。安全

餓漢式單例

  建立餓漢式單例簡單粗暴,在類被虛擬機加載時就建立類對象;併發

  缺點:可能在還不須要此實例的時候就已經把實例建立出來了,沒起到lazy loading的效果;jvm

  優勢:實現簡單,並且安全可靠;函數

package pattern.single;
/**
 * 餓漢式單例模式 不存在線程安全問題,由於在類加載時就建立好了對象
 * @author ningbeibei
 */
public class HungrySingle {
    // 當類加載時建立對象
    private static HungrySingle hungery = new HungrySingle();
    // 私有化構造函數,屏蔽外部建立對象
    private HungrySingle() {
    }
    // 提供全局訪問點,直接返回實例對象
    public static HungrySingle getHungrySingle() {
        return hungery;
    }
}

  測試類代碼測試

package pattern.single;
/**
 * 餓漢式單例模式測試類
 * @author ningbeibei
 */
public class test {
    public static void main(String[] args) {
        //餓漢式單例模式,獲取 HungrySingle 實例
        HungrySingle hungry = HungrySingle.getHungrySingle();
        System.out.println("HungrySingle對象:"+hungry);
    }
}

  運行結果spa

 

 懶漢式單例

   相比餓漢式,懶漢式實現了用則建立不用則不建立,真正實現了懶加載效果;線程

package pattern.single;
/**
  *  懶漢式單例模式
 * @author ningbeibei
 */
public class SinglePattern {
    //聲明對象變量
    private static SinglePattern single = null;
    //私有化構造函數
    private SinglePattern(){
    }
    //對外提供獲取對象得方法
    public static SinglePattern getSinglePattern() {
        if(single==null){
            return new SinglePattern();
        }
        return single;
    }
}

  注意:上面代碼實現了懶漢式單例,getSinglePattern()方法先判斷實例是否爲空再決定是否去建立實例,看起來彷佛很完美,可是存在線程安全問題。在併發獲取實例的時候,可能會存在構建了多個實例的狀況。因此,須要對此代碼進行下改進,確保實例惟一。設計

   改進後代碼code

package pattern.single;
/**
 * 雙重檢查加鎖
 * 線程安全懶漢模式
 * @author ningbeibei
 */
public class LockSinglePattern {
    //聲明變量, 使用volatile關鍵字確保絕對線程安全
    private volatile static LockSinglePattern lockSingle =null;
    //私有化構造函數
    private LockSinglePattern() {
    }
    //提供全局惟一獲取實例方法
    public static LockSinglePattern getLockSinglePattern() {
        //判斷實例是否null
        if(lockSingle==null) {
            //對單例類進行加鎖
            synchronized (LockSinglePattern.class) {
                //在判斷是否爲null
                if(lockSingle==null) {
                    //建立實例
                    lockSingle = new LockSinglePattern();
                }
            }
        }
        //返回實例
        return lockSingle;
    }
}

  注意:這裏採用了雙重校驗的方式,對懶漢式單例模式作了線程安全處理。經過加鎖,能夠保證同時只有一個線程走到第二個判空代碼中去,這樣保證了只建立 一個實例。這裏還用到了volatile關鍵字來修飾lockSingle,其最關鍵的做用是防止指令重排。

  測試類

package pattern.single;
/**
 * 懶漢式單例模式測試類
 * @author ningbeibei
 */
public class test {
    public static void main(String[] args) {
        //雙重加鎖,線程安全懶漢式單例模式
        LockSinglePattern lock= LockSinglePattern.getLockSinglePattern();
        System.out.println("線程安全懶漢式單例模式:"+lock);
    }
}

  運行結果

 

 

 

靜態內部類

  經過靜態內部類的方式實現單例模式是線程安全的,由於內部靜態類只會被加載一次,故該實現方式是線程安全的

  代碼以下:

package pattern.single;
/**
 * 靜態內部類實現單例模式
 * @author ningbeibei
 */
public class InteriorSingle {
    /**
     * 靜態內部類
     * @author ningbeibei
     */
    private static class Insingle {
        //靜態初始化器,由jvm來報證線程安全
        private static InteriorSingle single = new InteriorSingle();
    }
    //私有化構造函數
    private InteriorSingle() {
    }
    //提供全局惟一訪問點
    public static InteriorSingle getSingle() {
        return Insingle.single;
    }
}

   注意:經過靜態內部類的方式實現單例模式是線程安全的,同時靜態內部類不會在InteriorSingle類加載時就加載,而是在調用getSingle()方法時才進行加載,達到了懶加載的效果。彷佛靜態內部類看起來已是最完美的方法了,其實不是,可能還存在反射攻擊或者反序列化攻擊。

   測試類

package pattern.single;
/**
 * 懶漢式單例模式測試類
 * @author ningbeibei
 */
public class test {
    public static void main(String[] args) throws Exception {
        //靜態內部類實現單例模式。這個也式線程安全得
        InteriorSingle Interior = InteriorSingle.getSingle();
        System.out.println("靜態內部類單例模式:"+Interior);
    }
}

  運行結果

 

 

 枚舉實現單例模式

   最佳的單例實現模式就是枚舉模式。利用枚舉的特性,讓JVM來幫咱們保證線程安全和單一實例的問題。並且寫法還特別簡單。

   代碼以下

package pattern.single;
/**
 * 枚舉類
 * @author ningbeibei
 */
public enum TypeSingle {
    // 定義一個枚舉的元素,它 就表明了Singleton的一個實例
    TYPESINGLE;
    //業務方法
    public void get() {
        System.out.println("枚舉中的方法");
    }
}

  測試代碼

package pattern.single;
/**
 * 懶漢式單例模式測試類
 * @author ningbeibei
 */
public class test {
    public static void main(String[] args) throws Exception {
        //枚舉實現單例模式
        TypeSingle.TYPESINGLE.get();
    }
}

  運行結果

 

 

 

破壞單例的兩種方式

  反射破壞單例;

  序列化破壞單例;

  這兩中破壞單例模式的方式不會對枚舉單例方式構成威脅,因此通常都推薦枚舉實現單例模式;

總結

  以上列舉了多種單例模式的寫法,在不一樣的場景中有不一樣應用;

  寫的不足之處還望指正以便我及時更正避免讀者誤解;

相關文章
相關標籤/搜索