1. 程式人生 > 其它 >單例模式-Singleton

單例模式-Singleton

1、餓漢式:

/**
 * 單例:餓漢式
 * 缺點:可能會浪費空間
 */
public class Singleton_Hurry {
    private static final Singleton_Hurry HURRY = new Singleton_Hurry();

    //構造器私有化
    private Singleton_Hurry() {
        System.out.println("成功建立物件!"+System.currentTimeMillis());
    }

    /**
     * 提供公有方法來獲取例項
     * @return
     */
    public static Singleton_Hurry getInstance() {
        return HURRY;
    }

    public static void main(String[] args) {
        Singleton_Hurry h1 = Singleton_Hurry.getInstance();
        Singleton_Hurry h2 = Singleton_Hurry.getInstance();
        System.out.println(h1.hashCode()); //1265094477
        System.out.println(h2.hashCode()); //1265094477
    }
}

2、懶漢式:

/**
 * 單例:懶漢
 * 這種寫法在單執行緒上是可以,但在多執行緒下會出現併發問題
 */
public class Singleton_Lazy {
    //構造器私有化
    private Singleton_Lazy() {
        System.out.println(Thread.currentThread().getName()+"ok!");
    }

    private static Singleton_Lazy LAZY;

    public static Singleton_Lazy getInstance() {
        if (LAZY == null) {
            LAZY = new Singleton_Lazy();
        }
        return LAZY;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Singleton_Lazy.getInstance();
            }).start();
        }
    }
}


支援多執行緒的懶漢式:

/**
 * 單例:懶漢
 * 支援多執行緒(雙重檢測鎖)
 * 問題:不加 volatile會造成指令成排(可能建立不了例項)
 */
public class Singleton_Lazy {
    // 構造器私有化
    private Singleton_Lazy() {
        System.out.println(Thread.currentThread().getName() + "ok!");
    }

    // 避免指令重排
    private volatile static Singleton_Lazy lazy;

    public static Singleton_Lazy getInstance() {
        // 雙重檢測鎖模式的單例 (DCL)
        if (lazy == null) {
            synchronized (Singleton_Lazy.class) {
                if (lazy == null) {
                    lazy = new Singleton_Lazy();//它不是原子性操作
                    //1.分配記憶體空間 2.執行構造方法,初始化物件 3.把這個物件指向這個空間
                }
            }
        }
        return lazy;
    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Singleton_Lazy.getInstance();
            }).start();
        }
    }
}

3、靜態內部類

public class Holder {
    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }

    public static class InnerClass {
        private static final Holder HOLDER = new Holder();
    }
}

前三種單例會被反射破解:

public class Singleton_Lazy {
    // 構造器私有化
    private Singleton_Lazy() {
        System.out.println(Thread.currentThread().getName() + "ok!");
    }

    // 避免指令重排
    private volatile static Singleton_Lazy lazy;

    public static Singleton_Lazy getInstance() {
        // 雙重檢測鎖模式的單例 (DCL)
        if (lazy == null) {
            synchronized (Singleton_Lazy.class) {
                if (lazy == null) {
                    lazy = new Singleton_Lazy();//它不是原子性操作
                    //1.分配記憶體空間 2.執行構造方法,初始化物件 3.把這個物件指向這個空間
                }
            }
        }
        return lazy;
    }

    public static void main(String[] args) throws Exception {
        Singleton_Lazy lazy1 = Singleton_Lazy.getInstance();
        System.out.println(lazy1.hashCode());

        // 使用反射來破壞單例
        Constructor<Singleton_Lazy> constructor = Singleton_Lazy.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);//可以訪問私有的構造器
        Singleton_Lazy lazy2 = constructor.newInstance();
        System.out.println(lazy2.hashCode());
    }
}

解決單例被破解:

/**
* 三重檢測鎖
*/
public class Singleton_Lazy {
    // 構造器私有化
    private Singleton_Lazy(){
        synchronized (Singleton_Lazy.class) {
            if (lazy != null) {
               throw new RuntimeException("不要試圖使用反射破壞異常!");
            }
        }
    }

    // 避免指令重排
    private volatile static Singleton_Lazy lazy;

    public static Singleton_Lazy getInstance() {
        // 雙重檢測鎖模式的單例 (DCL)
        if (lazy == null) {
            synchronized (Singleton_Lazy.class) {
                if (lazy == null) {
                    lazy = new Singleton_Lazy();//它不是原子性操作
                    //1.分配記憶體空間 2.執行構造方法,初始化物件 3.把這個物件指向這個空間
                }
            }
        }
        return lazy;
    }

    public static void main(String[] args) throws Exception {
        Singleton_Lazy lazy1 = Singleton_Lazy.getInstance();
        System.out.println(lazy1.hashCode());

        // 使用反射來破壞單例
        Constructor<Singleton_Lazy> constructor = Singleton_Lazy.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);//可以訪問私有的構造器
        Singleton_Lazy lazy2 = constructor.newInstance();
        System.out.println(lazy2.hashCode());
    }
}

如果正常建立物件也不是單例:

public class Singleton_Lazy {
    // 構造器私有化
    private Singleton_Lazy(){
        synchronized (Singleton_Lazy.class) {
            if (lazy != null) {
               throw new RuntimeException("不要試圖使用反射破壞異常!");
            }
        }
    }

    // 避免指令重排
    private volatile static Singleton_Lazy lazy;

    public static Singleton_Lazy getInstance() {
        // 雙重檢測鎖模式的單例 (DCL)
        if (lazy == null) {
            synchronized (Singleton_Lazy.class) {
                if (lazy == null) {
                    lazy = new Singleton_Lazy();//它不是原子性操作
                    //1.分配記憶體空間 2.執行構造方法,初始化物件 3.把這個物件指向這個空間
                }
            }
        }
        return lazy;
    }

    public static void main(String[] args) throws Exception {
        Singleton_Lazy lazy1 = Singleton_Lazy.getInstance();
        System.out.println(lazy1.hashCode());

        // 使用反射來破壞單例
        /*Constructor<Singleton_Lazy> constructor = Singleton_Lazy.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);//可以訪問私有的構造器
        Singleton_Lazy lazy2 = constructor.newInstance();
        System.out.println(lazy2.hashCode());*/

        Singleton_Lazy lazy3 = Singleton_Lazy.getInstance();
        System.out.println(lazy3.hashCode());
    }
}

解決方法: