定義:在一個jvm中只能存在一個實例,保證對象唯一性。
應用場景:servlet、struts2、springMVC、連接池、線程池、枚舉、常量。
優點:節約內存、方便管理、重複利用。
缺點:線程不安全
創建方式:
- 餓漢模式:類初始化的時候會立即創建該對象(存放在方法區中,不會被垃圾回收機制回收),線程天生安全(因爲final修飾的全局變量),調用效率高,但是佔內存:
public class Singleton {
public static final Singleton signleton = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return signleton;
}
}
-
懶漢模式:類初始化的時候不會創建該對象,只有當需要使用該對象的時候纔會創建,具有懶加載功能,但是線程不安全需要自己加鎖,所以效率低,會阻塞、等待:
public class Singleton {
private static Singleton signleton;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(null == signleton) {
signleton = new Singleton();
}
return signleton;
}
} -
靜態內部類:結合懶漢和餓漢模式的優點,真正需要對象的時候纔會加載,且線程安全: public class Singleton {
private Singleton(){
}
public static class SingletonInstance{
public static final Singleton singleton = new Singleton();
}
public static Singleton getInstance(){
return SingletonInstance.singleton;
}
} -
枚舉單例:實現簡單,調用效率高,枚舉本身就是單例: 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;
}
}
} -
雙重檢驗鎖:因爲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));
}
}
這裏可以隨機指定一個實例進行處理,當然“池”的實現肯定比這個要複雜的多。