今天把設計模式拿出來看了下,發現以前對於單例模式的理解很是膚淺,沒考慮線程安全,在考慮了線程安全的情況下又沒考慮性能,當然我遇到的系統都不怎么考慮並發性能,所以其實也無所謂,當看到單例模式的時候,上網搜索了下,發下一片很好的帖子,我把其中的單例模式整理了下,給了個結果出來。
帖子地址:http://blog.csdn.net/zhangerqing/article/details/8194653
前提都是在考慮線程安全的情況下,可以有兩種情況。分別是懶漢式和餓漢式的一種實現。
1.性能不太好,每次鎖對象,當然如果你遇到的跟我遇到的系統都一樣不考慮性能問題,其實這么做也沒問題,但是不好。正如Oracle高級編程里面所說的那樣,你的sql能正確的完成任務,但並不代表他能很好的完成任務。
2.我們只希望在第一次getInstance的時候去實例化對象,這個就做到了,並且線程安全,但是如果類加載的時候出問題還是有可能出問題,所以借用那篇帖子里說的,要完成完美的實現是不可能的,我們只能根據實際情況來選擇合適的方式。
1.直接鎖整個對象,保證線程安全,但是由於每次獲取都會鎖對象,性能不好,也不是我們希望的。
package singlePattern; /** * Thread safe single pattern, but performance is poor, because every time we will lock the instance, * we hope lock the instance on first time but all times * * @author Administrator * */ public class SinglePattern { // private instance private static SinglePattern instance = null; // private constructor private SinglePattern() { } // public getInstance method public synchronized static SinglePattern getInstance() { if (instance == null) { instance = new SinglePattern(); } return instance; } }
2.我們需要的情形是在第一次初始化需要同步,之后的調用就不需要考慮線程同步。由於JVM在加載類的時候是互斥的,我們知道類加載器在加載類的時候會直接把靜態變量和靜態代碼塊執行一次,然后再去去調用普通代碼快,然后去調用構造方法初始化堆,然后在返回引用賦值給棧里面的引用。於是我們可以利用類加載的靜態變量和靜態代碼塊來安全的構造單例實例。
package singlePattern; /** * Thread safe and performance good. * @author Administrator * */ public class UserSinglePattern { // private constructor private UserSinglePattern() {} // static inner class, for create a single pattern instance private static class NewInstance { private static UserSinglePattern instance = new UserSinglePattern(); } // the get method for single pattern instance public static UserSinglePattern getInstance() { return NewInstance.instance; } }
3.如果實例采用靜態的就能實現在類加載的時候線程安全的初始化實例,但是卻不能實現lazy加載,而采用上面的靜態內部類的方式就能實現lazy加載,因為虛擬機機制是需要調用getInstance()方法的時候才會加載靜態內部類,如果不調用那么他是不會被加載的。下面是測試代碼,同時擁有2個靜態內部類來創建對象,但是只調用其中一個那么就只會加載一個,另外一個是不會被加載的。
public class SinglePatternThreadSafe { // 私有構造方法 private SinglePatternThreadSafe() { System.out.println(123); } private SinglePatternThreadSafe(String str) { System.out.println(321); } // 內部類,利用類加載的互斥性來達到單例的創建 private static class NewInstance { private static SinglePatternThreadSafe instance = new SinglePatternThreadSafe(); } private static class NewInstance1 { private static SinglePatternThreadSafe in = new SinglePatternThreadSafe("11"); } // 返回實例的公共方法 public static SinglePatternThreadSafe getInstance() { return NewInstance.instance; } public static SinglePatternThreadSafe getInstance(String str) { return NewInstance1.in; } public static void main(String[] args) { SinglePatternThreadSafe.getInstance(); }
// 只打印出123而沒有打印11 }
再貼下我參考的地址對作者表示感謝:http://blog.csdn.net/zhangerqing/article/details/8194653
這個也寫的很好:http://www.cnblogs.com/coffee/archive/2011/12/05/inside-java-singleton.html
這篇也測試了如果在不調用靜態內部類的方法或者變量或者常量的時候內部類是不會被加載的,http://yongliang567.iteye.com/blog/904467
