單例模式

定義:在一個jvm中只能存在一個實例,保證對象唯一性。

應用場景:servlet、struts2、springMVC、連接池、線程池、枚舉、常量。

優點:節約內存、方便管理、重複利用。

缺點:線程不安全

創建方式:

  1. 餓漢模式:類初始化的時候會立即創建該對象(存放在方法區中,不會被垃圾回收機制回收),線程天生安全(因爲final修飾的全局變量),調用效率高,但是佔內存:

    public class Singleton {
        public static final Singleton signleton = new Singleton();
        private Singleton(){

        }

        public static Singleton getInstance(){
            return signleton;
        }
    }
     

  2. 懶漢模式:類初始化的時候不會創建該對象,只有當需要使用該對象的時候纔會創建,具有懶加載功能,但是線程不安全需要自己加鎖,所以效率低,會阻塞、等待:

    public class Singleton {
        private static Singleton signleton;
        private Singleton(){

        }

        public static synchronized Singleton getInstance(){
            if(null == signleton) {
                signleton = new Singleton();
            }
            return signleton;
        }
    }

  3. 靜態內部類:結合懶漢和餓漢模式的優點,真正需要對象的時候纔會加載,且線程安全:                                             public class Singleton {
        private Singleton(){
            
        }
        public static class SingletonInstance{
            public static final Singleton singleton = new Singleton();
        }
        public static Singleton getInstance(){
            return SingletonInstance.singleton;
        }
    }

  4. 枚舉單例:實現簡單,調用效率高,枚舉本身就是單例:                                                                                               public class Singleton {
        private Singleton(){
            System.out.println(1);
        }
        public static Singleton getInstance(){
            return SingletonEnum.INSTATNCE.getInstance();
        }
       static enum SingletonEnum{
            INSTATNCE;
            private Singleton singleton;
            private SingletonEnum(){
                singleton = new Singleton();
           }
           public Singleton getInstance(){
                return this.singleton;
           }
       }
    }

  5. 雙重檢驗鎖:因爲jvm本質重排序,可能會導致多次初始化,所以不推薦使用:                                                             public class Singleton {
        public static Singleton signleton;
        private Singleton(){
            System.out.println(1);
        }
        public static Singleton getInstance(){
            if(signleton == null){
                synchronized (Singleton.class){
                    if (signleton == null){
                        signleton = new Singleton();
                    }
                }
            }
            return signleton;
        }
    }

單例模式的核心思想就是控制創建實例對象,通過將構造方法私有化來防止多次創建對象的操作,但是我們可以通過反射機制來訪問私有構造器,那麼我們可以在私有構造器上加一個判斷,如果對象已經被創建則拋出異常信息(或者其他處理),記得加鎖,保證只能有一個訪問對象。代碼如下:

Class Sigleton{
    private static boolean FLAG = false;
    private Sigleton(){
          synchronied(Sigleton.Class){
            if(!FLAG){
                FLAG = !FLAG;

                //創建對象
            }ELSE{
                throw new RuntimeException("該對象是單例,不允許重複創建“);
            }
        }
    }
}

這裏只介紹了5種單例的創建方式,剩下的以後再補充。

看了《設計模式之禪》發現了單例模式的一種擴展形式,可以限制指定生成對象的數量,個人理解是不是有點像“池”的概念,比如數據庫連接池設置的最大連接數,擴展代碼如下:

public class Singleton {
    //定義最多能產生的實例數量
    private static int maxNumOfSingleton = 2;
    //每個實例都有名字,使用一個ArrayList來容納,每個對象的私有屬性
    private static ArrayList<String> nameList = new ArrayList<String>();
    //定義一個列表,容納所有的實例
    private static ArrayList<Singleton> emperorList = new ArrayList<Singleton>();
    //當前實例序列號
    private static int countNumOfSingleton = 0;
    //產生所有的對象
    static {
        for (int i = 0; i < maxNumOfSingleton; i++) {
            emperorList.add(new Singleton( (i + 1) + ""));
        }
    }
    private Singleton(String name){
        nameList.add(name);
    }

    public static Singleton getInstance(){
        Random random = new Random();
        //隨機獲取一個實例
        countNumOfSingleton = random.nextInt(maxNumOfSingleton);
        return emperorList.get(countNumOfSingleton);
    }

    public static void say() {
        System.out.println(nameList.get(countNumOfSingleton));
    }
}

這裏可以隨機指定一個實例進行處理,當然“池”的實現肯定比這個要複雜的多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章