您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“Java中單例模式的示例分析”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“Java中單例模式的示例分析”這篇文章吧。
懶漢模式與餓漢模式
懶漢模式就是懶加載,用到的時候去加載,存在線程安全問題,需要手動地加鎖控制。它的優點是類加載的速度比較快,按需加載,節省資源。
餓漢模式就是在類加載的時候會創建出實例。它天生就不存在線程安全問題。但是類加載的速度會變慢且耗費資源。
懶漢模式-單重檢查
示例代碼如下:
public class LazySingleton { private static LazySingleton singletoninstance = null; private Object data = new Object(); //私有化構造方法 private LazySingleton(){ } //加鎖訪問 public static synchronized LazySingleton getInstance(){ if(singletoninstance == null){ singletoninstance = new LazySingleton(); } return singletoninstance; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
測試代碼如下:
public class TestThread extends Thread { @Override public void run() { LazySingleton instance = LazySingleton.getInstance(); System.out.println(instance.getData()); } } public static void main(String[] args) { for(int i =0;i < 10;i++){ TestThread t = new TestThread(); t.start(); } } }
運行結果如下:
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
打印出同一個object對象,表明是從同一個LazySingleton對象中獲取的數據。
但是上述代碼存在一個顯著的問題:多個線程同時訪問getInstance()方法都是排隊式的,即使該instance已經被創建的情況下。然而,如果該instance已經被創建,是可以支持并發訪問的。需要對鎖的控制細粒度化。
懶漢模式-雙重檢查
public class LazySingleton { //聲明為volatile變量 private static volatile LazySingleton singletoninstance = null; private Object data = new Object(); private LazySingleton(){ } public static synchronized LazySingleton getInstance(){ if(singletoninstance == null){ synchronized (LazySingleton.class) { //這個第二重的的檢查是必要的 if(singletoninstance == null) singletoninstance = new LazySingleton(); } } return singletoninstance; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
第二重檢查是為了防止:
線程A發現instance未被創建,于是申請鎖,進入臨界區創建instance;于此同時另一個線程也發現instance未被創建,于是也要申請鎖去創建instance,問題就這樣發生了。而且,這個instance變量要被聲明為volatile,也就是其中一個線程對它就行修改之后(也就是實例化),這一修改立馬對其他線程可見,避免了無謂的等待。
檢查代碼同上,運行結果同上。
餓漢模式
public class HungerSingleton { private static final HungerSingleton singletoninstance = new HungerSingleton(); private Object data = new Object(); private HungerSingleton(){ } public static HungerSingleton getInstance(){ return singletoninstance; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
在加載該類的時候就立馬去實例化instance,不存在線程安全問題(由jvm保證線程安全問題),但是存在資源浪費、加載速度慢的問題。
檢查代碼同上,運行結果同上。
Holder模式
就是利用一個靜態內部類來實現instance的實例化。這里利用了靜態內部類的一個特性:該內部類的實例與外部類的實例 沒有綁定關系,而且只有被調用到才會裝載,從而實現了延遲加載
public class HolderSingleton { private Object data = new Object(); private HolderSingleton(){ } private static class InnerClass{ private static HolderSingleton singletoninstance = new HolderSingleton(); } public static HolderSingleton getInstance(){ return InnerClass.singletoninstance; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
測試代碼同上,運行結果同上。
在加載InnerClass的時候才會去實例化這個instance,實現了延遲加載,并且同餓漢模式一樣,由jvm保證線程安全。這種方法值得推薦。
應用場景:
在整個系統中,只允許共用一個實例的類適合用單例模式來實現,比如:
網站的計數器,只允許存在一個計數器實例;
線程池,只允許存在一個線程池對象;
連接池,只允許存在一個連接池對象;
知識點擴充:
1.為什么要使用單例模式?
在我們日常的工作中,很多對象通常占用非常重要的系統資源,比如:IO處理,數據庫操作等,那我們必須要限制這些對象只有且始終使用一個公用的實例,即單例。
2.單例模式的實現方式
構造函數私有化,防止其他類生成唯一公用實例外的實例。且單例類應該被定義為final,也就是說單例類不能被繼承,因為如果允許繼承那子類就都可以創建實例,違背了類唯一實例的初衷。
類中一個靜態變量來保存單實例的引用。
一個共有的靜態方法來獲取單實例的引用。
3.單例模式的UML類圖
4.單例模式的經典實現方式
餓漢式:一開始就創建好實例,每次調用直接返回,經典的“拿空間換時間”。
懶漢式:延遲加載,第一次調用的時候才加載,然后返回,以后的每次的調用就直接返回。經典“拿時間換空間”,多線程環境下要注意解決線程安全的問題。
登記式:對一組單例模式進行的維護,主要是在數量上的擴展,通過線程安全的map把單例存進去,這樣在調用時,先判斷該單例是否已經創建,是的話直接返回,不是的話創建一個登記到map中,再返回。
以上是“Java中單例模式的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。