設計模式(一)------ 單例模式

1、設計模式分類

建立型模式
     - 單例模式工廠模式、抽象工廠模式、建造者模式、原型模式
結構型模式
    - 適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式
行爲型模式
    - 模板方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、解釋器模式、狀態模式、策略模式、職責鏈模式、訪問者模式。設計模式

2、單例模式

核心做用:保證一個類只有一個實例,而且提供一個訪問該實例的全局訪問點。安全

單例模式優勢:因爲單例模式只生成一個實例,減小系統性能開銷,當一個對象的產生須要比較多的資源時,如讀取配置文件,則能夠經過在應用啓動時直接產生一個單例對象,而後永久駐留在內存的方式解決。多線程

常見的五種單例模式實現方式併發

  餓漢式(線程安全,調用效率高。可是不能延時加載)性能

  懶漢式(線程安全,調用效率不高。可是能夠延時加載)測試

  雙層檢測鎖式(因爲JVM底層內部模型緣由,偶爾出現問題,不建議使用)spa

  靜態內部類式(線程安全、調用效率高,能夠延時加載)線程

  枚舉單例(線程安全,調用效率高,不能延時加載,自然的防止反射和反序列化漏洞)設計

 

一、餓漢式代理

/** * 餓漢式單例模式 * static 變量會在類加載時初始化。JVM保證只會加載一次該類, 確定不會發生併發訪問的問題 */
public class SingleDemo1 { // 類初始化時加載這個對象
    private static /*final*/ SingleDemo1 INSTANCE = new SingleDemo1(); // 構造方法私有
    private SingleDemo1() {} 
  // 方法沒有同步, 調用效率高
public static SingleDemo1 getInstance() { return INSTANCE; } }

 

二、懶漢式

/** * 懶漢式 * 延時加載, 資源利用率高; * getInstance() 增長同步方法, 調用效率低 */
public class SingleDemo2 { private static SingleDemo2 singleDemo2; // 構造方法私有化
    private SingleDemo2() {} // 加synchronized方法同步, 調用效率低
    public static synchronized SingleDemo2 getInstance() { if(null == singleDemo2) { singleDemo2 = new SingleDemo2(); } return singleDemo2; } }

 

三、雙重檢測鎖模式

 

 四、靜態內部類實現單例模式

/** * 靜態內部類實現單例模式 * 線程安全, 懶加載, 調用效率高 */
public class SingleDemo3 { // 靜態內部類
    private static class SingleDemoInner { //類初始化時加載, static final 保證內存中只有一個這樣實例存在, 並且只能被賦值一次
        private static final SingleDemo3 INSTANCE = new SingleDemo3(); } // 構造方法私有化
    private SingleDemo3() {} // 沒有同步, 調用效率高
    public static SingleDemo3 getInstance() { return SingleDemoInner.INSTANCE; } }

五、枚舉類

/** * 枚舉類實現單例模式 (不能延時加載) * 枚舉是自然的單例, 由JVM從根本上提供保障; * 避免經過反射和反序列化的漏洞! */
public enum SingleDemo4 { // 定義一個枚舉的元素, 它就表明了Singleton的一個實例
 INSTANCE; //單例能夠有本身的操做
    public void singletonOperation() { //功能處理
 } }

 

六、使用反射或反序列化能夠破解上面的懶漢式、餓漢式、雙重檢測鎖模式(不建議使用)、靜態內部類式實現的單例。

(1)測試反射破解:

/** * 測試反射破解 */
public class MainTest { public static void main(String[] args) throws Exception { // 經過反射方式構造多個對象
        Class<SingleDemo1> clazz = (Class<SingleDemo1>) Class.forName("com.yufeng.single.SingleDemo1"); Constructor<SingleDemo1> constructor= clazz.getDeclaredConstructor(null); constructor.setAccessible(true); //跳過權限檢查
 SingleDemo1 demo3 = constructor.newInstance(); SingleDemo1 demo4 = constructor.newInstance(); System.out.println(demo3); System.out.println(demo4); } }

結果:

  com.yufeng.single.SingleDemo1@14ae5a5
  com.yufeng.single.SingleDemo1@7f31245a

(2)測試反序列化破解(SingleDemo1 實現 Serializable 接口)

/** * 測試反序列化破解 */
public class MainTest { public static void main(String[] args) throws Exception { // 經過反序列化構造多個對象
        SingleDemo1 a1 = SingleDemo1.getInstance(); //序列化的類要實現 Serializable 接口 System.out.println(a1); //序列化
        FileOutputStream fos = new FileOutputStream("d:/a.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(a1); oos.close(); fos.close(); //反序列化
        FileInputStream fis = new FileInputStream("d:/a.txt"); ObjectInputStream ois = new ObjectInputStream(fis); SingleDemo1 a2 = (SingleDemo1) ois.readObject(); System.out.println(a2); } }

 

 解決反射和反序列化破解單例的問題

以懶漢式爲例,以下:

/** * 解決反射和反序列化破解單例的問題 */
public class SingleDemo5 implements Serializable { private static /*final*/ SingleDemo5 INSTANCE = new SingleDemo5(); // 構造方法私有
    private SingleDemo5() { //防止反射破壞單例 能夠在構造方法中手動拋出異常,解決反射破解的問題
        if(null != INSTANCE) { throw new RuntimeException("單例模式, 不可以使用反射建立實例."); } } // 方法沒有同步, 調用效率高
    public static SingleDemo5 getInstance() { return INSTANCE; } 
// 能夠經過readResolve()方法防止反序列得到到不一樣的對象
// 反序列化時, 若定義了readReolve()方法, 直接返回此方法指定的對象, 而不須要再單首創建新對象 private Object readResolve() throws ObjectStreamException { return INSTANCE; } }

 

 3、五種實現方式在多線程下的效率

相關文章
相關標籤/搜索