我平常再寫單例模式時經常用這兩種方法:
方法一:
public class SingletonDome { //私有的構造方法是創建單例的基礎 private SingletonDome(){ System.out.println("singletonDome create"); } private static SingletonDome singleton = new SingletonDome(); public static SingletonDome getInstance(){ return singleton; } }
方法二:
public class SingletonDome { private SingletonDome(){ System.out.println("singletonDome create"); } private static SingletonDome singleton = null; public static synchronized SingletonDome getInstance(){ if(singleton==null){ singleton = new SingletonDome(); } return singleton; } }
第一種方法,在 JVM 加載單例類時,單例對象就會被建立,如果此時這個單例類在系統中還扮演其他角色,那麼在任何使用這個單例類的地方都會初始化這個單例變量,而不管是否會被用到。
缺點不能延遲加載
第二種方法,當用戶第一次需要這個對象時才進創建,並時線程安全的,但是由於synchronized性能缺陷(之前我真沒什麼感覺..見測試用例)
public class LazySingleton implements Runnable{ private LazySingleton(){ System.out.println("LazySingleton is create"); } private static LazySingleton instance = null; public static synchronized LazySingleton getInstance(){ if(instance == null){ instance = new LazySingleton(); } return instance; } public static void createString(){ System.out.println("create String"); } @Override public void run() { // TODO Auto-generated method stub long beginTime = System.currentTimeMillis(); for(int i=0;i<1000000;i++){ LazySingleton.getInstance(); } System.out.println(System.currentTimeMillis() - beginTime); } public static void main(String[] args){ //LazySingleton.createString(); for(int i=0;i<5;i++){ new Thread(new LazySingleton()).start(); } } } 打印: LazySingleton is create LazySingleton is create LazySingleton is create LazySingleton is create LazySingleton is create LazySingleton is create 290 593 585 598 603
今天發現兩種新的單例的寫法:
靜態內部類和枚舉
靜態內部類寫法:
public class SingletonDome { private SingletonDome(){ System.out.println("singletonDome create"); } private static class SingletonHolder{ private static SingletonDome instance = new SingletonDome(); } public static SingletonDome getInstance(){ return SingletonHolder.instance; } }
測試:
public class LazySingleton implements Runnable { private LazySingleton() { System.out.println("LazySingleton is create"); } private static class SingletonHolder { private static LazySingleton instance = new LazySingleton(); } public static LazySingleton getInstance() { return SingletonHolder.instance; } public static void createString() { System.out.println("create String"); } @Override public void run() { // TODO Auto-generated method stub long beginTime = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { LazySingleton.getInstance(); } System.out.println(System.currentTimeMillis() - beginTime); } public static void main(String[] args) { // LazySingleton.createString(); for (int i = 0; i < 5; i++) { new Thread(new LazySingleton()).start(); } } } 運行效果如下: LazySingleton is create LazySingleton is create LazySingleton is create LazySingleton is create LazySingleton is create LazySingleton is create 1 3 1 1 1
備註:
當 SingletonDome 被加載時,其內部類並不會被初始化,故可以確保當 SingletonDome 類被載入 JVM 時,不會初始化單例類,
而當 getInstance() 方法調用時,纔會加載 SingletonHolder,從而初始化 instance。
同時,由於實例的建立是時在類加載時完成,故天生對多線程友好,getInstance() 方法也無需使用同步關鍵字。
枚舉方式:
public enum AnimalHelperSingleton { INSTANCE; private AnimalHelperSingleton(){ } public Animal[] buildAnimalList(){ final Animal[] animals = new Animal[10]; animals[0] = new SimpleAnimal(Animal.AnimalClass.MAMMAL, "Dog", true, Color.GRAY); animals[1] = new SimpleAnimal(Animal.AnimalClass.MAMMAL, "Cat", true, Color.YELLOW); animals[2] = new SimpleAnimal(Animal.AnimalClass.AMPHIBIAN, "Frog", true, Color.GREEN); animals[3] = new SimpleAnimal(Animal.AnimalClass.BIRD, "Crow", true, Color.BLACK); animals[4] = new SimpleAnimal(Animal.AnimalClass.BIRD, "Cardinal", true, Color.RED); animals[5] = new SimpleAnimal(Animal.AnimalClass.ARTHROPOD, "Mantis", false, Color.GREEN); animals[6] = new SimpleAnimal(Animal.AnimalClass.ARTHROPOD, "Spider", false, Color.ORANGE); animals[7] = new SimpleAnimal(Animal.AnimalClass.MAMMAL, "Tiger", true, Color.ORANGE); animals[8] = new SimpleAnimal(Animal.AnimalClass.MAMMAL, "Bear", true, Color.BLACK); animals[9] = new SimpleAnimal(Animal.AnimalClass.BIRD, "Owl", true, Color.BLACK); return animals; } } 調用方式:Animal[] animals = AnimalHelperSingleton.INSTANCE.buildAnimalList();
優點:
1、 自由序列化;
2、 保證只有一個實例(即使使用反射機制也無法多次實例化一個枚舉量);
3、 線程安全;
話說寫單例用枚舉的很少哎,而且感覺不是那麼好理解。。。
單例--靜態內部類摘自:http://www.ibm.com/developerworks/cn/java/j-lo-Singleton/
單例--枚舉方式摘自:http://blog.csdn.net/horace20/article/details/37562513
大家可以去看一下原文哦。