單例模式,顧名思義就是一個類只有一個實例,而且類負責建立本身的對象,這個類提供了一種訪問其惟一的對象的方式,能夠直接訪問,不須要實例化該類的對象。編程
從面向對象的角度講:安全
雖然都能實現目的,可是他們一個是基於對象,一個是面向對象的,就像咱們不面相對象也能解決問題同樣,面相對象的代碼提供一個更好的編程思想。多線程
若是一個方法和他所在類的實例對象無關,那麼它就應該是靜態的,反之他就應該是非靜態的。若是咱們確實應該使用非靜態的方法,可是在建立類時又確實只須要維護一份實例時,就須要用單例模式了。函數
好比說咱們在系統運行時候,就須要加載一些配置和屬性,這些配置和屬性是必定存在了,又是公共的,同時須要在整個生命週期中都存在,因此只須要一份就行,這個時候若是須要我再須要的時候new一個,再給他分配值,顯然是浪費內存而且再賦值沒什麼意義,因此這個時候咱們就須要單例模式或靜態方法去維持一份且僅這一份拷貝,但此時這些配置和屬性又是經過面向對象的編碼方式獲得的,咱們就應該使用單例模式,或者不是面向對象的,但他自己的屬性應該是面對對象的,咱們使用靜態方法雖然能一樣解決問題,可是最好的解決方案也應該是使用單例模式。性能
從功能上講:單例模式能夠控制單例數量;能夠進行有意義的派生;對實例的建立有更自由的控制;編碼
餓漢式spa
簡單懶漢式(在方法加鎖)線程
DCL雙重檢測加鎖(進階懶漢式)code
靜態內部類實現懶漢式(最推薦寫法)對象
枚舉方式(最安全、簡潔寫法)
將構造函數私有化
在類的內部建立實例
提供獲取惟一實例的方法
1 public class model1 { 2 //1.將構造函數私有化,不能夠經過new的方式來建立對象 3 private model1(){}; 4 //2.在類的內部建立實例 5 private static model1 model1 = new model1(); 6 //3.提供獲取惟一實例的方法 7 public static model1 getLl(){ 8 return model1; 9 } 10 }
這種方法一上來就建立對象,若是一直用不到則形成資源浪費
1 public class model2 { 2 //1.將構造函數私有化,不能夠經過new的方式來建立對象 3 private model2(){}; 4 //2.先不建立對象,等用到的時候再建立 5 private static model2 model2 = null; 6 //3.提供獲取惟一實例的方法,當這個方法被調用,說明用到了 7 public static model2 getLl(){ 8 //若是model2尚未別建立,則建立,若是已經有了,直接返回 9 if (model2 == null){ 10 model2=new model2(); 11 } 12 return model2; 13 } 14 }
這種方法是在用到的時候再建立對象,但須要注意的是,這種方法在多線程環境下須要加鎖,以下:
1 public class model2 { 2 //1.將構造函數私有化,不能夠經過new的方式來建立對象 3 private model2(){}; 4 //2.先不建立對象,等用到的時候再建立 5 private static model2 model2 = null; 6 //3.提供獲取惟一實例的方法,當這個方法被調用,說明用到了 7 public static synchronized model2 getLl(){ 8 //若是model2尚未別建立,則建立,若是已經有了,直接返回 9 if (model2 == null){ 10 model2=new model2(); 11 } 12 return model2; 13 } 14 }
若是採用上面的方法,在方法上加鎖,在多線程環境下性能比較低,因此將鎖的範圍下降
1 public class model2 { 2 //1.將構造函數私有化,不能夠經過new的方式來建立對象 3 private model2(){}; 4 //2.先不建立對象,等用到的時候再建立 5 private static model2 model2 = null; 6 //3.提供獲取惟一實例的方法,當這個方法被調用,說明用到了 7 public static model2 getLl(){ 8 //若是model2尚未別建立,則建立,若是已經有了,直接返回 9 if (model2 == null){ 10 synchronized(model2.getClass()){ 11 model2=new model2(); 12 } 13 } 14 return model2; 15 } 16 }
將範圍下降後會出現一個問題:
線程A和線程B同時調用getLl()方法,他們同時判斷model2==null
,進入了if代碼塊了
此時線程A獲得CPU的控制權-->進入同步代碼塊-->建立對象-->返回對象
線程A完成了之後,此時線程B獲得了CPU的控制權。一樣是-->進入同步代碼塊-->建立對象-->返回對象
很明顯的是:該類返回的不是一個實例,因此上面的代碼是不行的!
解決方法是在進入同步代碼塊時在判斷一下對象是否存在
1 public class model2 { 2 //1.將構造函數私有化,不能夠經過new的方式來建立對象 3 private model2(){}; 4 //2.先不建立對象,等用到的時候再建立 5 private static model2 model2 = null; 6 //3.提供獲取惟一實例的方法,當這個方法被調用,說明用到了 7 public static model2 getLl(){ 8 //若是model2尚未別建立,則建立,若是已經有了,直接返回 9 if (model2 == null){ 10 synchronized(model2.getClass()){ 11 if (model2==null){ 12 model2=new model2(); 13 } 14 } 15 } 16 return model2; 17 } 18 }
最後還須要在對象上加volatile關鍵字,防止出現重排序問題
1 public class model2 { 2 //1.將構造函數私有化,不能夠經過new的方式來建立對象 3 private model2(){}; 4 //2.先不建立對象,等用到的時候再建立 5 private static volatile model2 model2 = null; 6 //3.提供獲取惟一實例的方法,當這個方法被調用,說明用到了 7 public static model2 getLl(){ 8 //若是model2尚未別建立,則建立,若是已經有了,直接返回 9 if (model2 == null){ 10 synchronized(model2.getClass()){ 11 if (model2==null){ 12 model2=new model2(); 13 } 14 } 15 } 16 return model2; 17 } 18 }
1 public class model3 { 2 private model3(){}; 3 //使用內部類的方式來實現懶加載 4 private static class LazyHolder{ 5 //建立單例對象 6 private static final model3 INSTANCE=new model3(); 7 } 8 public static final model3 getInstance(){ 9 return LazyHolder.INSTANCE; 10 } 11 }
1 public enum model4 { 2 MODEL_4, 3 }
這種方法簡單,能夠防止屢次實例,也是比較推薦的一種.