單例的目的是保證某個類僅有一個實例。當有某些類創建對象內存開銷較大時可以考慮使用該模式。單例模式又分為 餓漢式 和 懶漢式 。下面分別說明:
1.餓漢式。顧名思義,該模式在類被加載時就會實例化一個對象。具體代碼如下:
public class Person { //餓漢式單例 private static Person person = new Person(); private Person(){} public static Person getInstance(){ return person; } }
該模式能簡單快速的創建一個單例對象,而且是線程安全的(只在類加載時才會初始化,以后都不會)。但它有一個缺點,就是不管你要不要都會直接創建一個對象,會消耗一定的性能(當然很小很小,幾乎可以忽略不計,所以這種模式在很多場合十分常用而且十分簡單)
2.懶漢式。該模式只在你需要對象時才會生成單例對象(比如調用getInstance方法)
public class User { //懶漢式單例,只有在調用getInstance時才會實例化一個單例對象 public static User user; private User(){ } public static User getInstance(){ if(user==null){ //step 1. user = new User(); //step 2 } return user; } }
看上去,這段代碼沒什么明顯問題,但它不是線程安全的。假設當前有N個線程同時調用getInstance()方法,由於當前還沒有對象生成,所以一部分同時都進入step 2,
那么就會由多個線程創建多個多個user對象。
解決辦法:使用synchronized關鍵字。經改造上面代碼展示如下:
public class User { //懶漢式單例,只有在調用getInstance時才會實例化一個單例對象 public static User user; public static Integer key = new Integer(4); //作為一個鎖 private User(){ } public static User getInstance(){ //先判斷該user變量是否為空,入股為空,進入同步代碼塊,該步假設為step1 if(user == null){ //step 1 //想象一下,如果不判斷,那么每次訪問這個方法不管該對象是否已經創建都要進入同步代碼塊,線程數一多,資源消耗也是非常巨大的。 synchronized (key) { //由於可能多個線程都進入了step1,由於鎖定機制,一個線程進入該代碼塊時,其他線程 //仍在排隊進入該代碼塊,如果不做判斷,當前線程即使創造了實例,下一個線程也不知道,就會繼續創建一個實例 if(user==null){ user = new User(); } } } return user; } }
通過上面的鎖機制同步代碼塊就可以寫出線程安全的懶漢式單例。
參考文檔:https://blog.csdn.net/SummerMangoZz/article/details/57080540