單例設計模式是在軟件系統中采用一定的方法,保證某個類只能存在一個實例對象,並且該類只能有一個靜態方法來獲取該對象。
注意下面各類實現方式中的測試代碼都一樣:需要注意導入的包路徑即可。
package com.yefengyu; import com.yefengyu.type2.Singleton; public class Client { public static void main(String[] args) { Singleton singleton1 = Singleton.getInstance(); Singleton singleton2 = Singleton.getInstance(); System.out.println(singleton1 == singleton2); } }
1、實現方式一
該方式是靜態常量實現的餓漢式(類加載的時候便創建了實例):
package com.yefengyu.type1; public class Singleton { //類內部實例化 private final static Singleton INSTANCE = new Singleton(); //構造器私有化,防止new對象 private Singleton() { } //對外提供公有方法調用 public static Singleton getInstance() { return INSTANCE; } }
1、類加載的時候實例化,防止多線程問題。
2、沒有使用懶加載,類加載就產生對象,如果始終未使用則造成內存浪費。但是該對象只有一個,浪費空間也不是很大,可以使用,編寫的時候非常簡單。
2、實現方式二
該方式是靜態代碼塊實現的餓漢式
package com.yefengyu.type2; public class Singleton { private final static Singleton INSTANCE; static { //使用靜態代碼塊生成對象 INSTANCE = new Singleton(); } //構造器私有化,防止new對象 private Singleton() { } //對外提供公有方法調用 public static Singleton getInstance() { return INSTANCE; } }
該方式和方式一的優缺點類似。
3、實現方式三
該方式是同步方法實現懶漢式(只有第一次使用的時候才創建實例)
package com.yefengyu.type3; public class Singleton { private static Singleton instance; //構造器私有化,防止new對象 private Singleton() { } //只有在第一次使用的時候構造實例對象,使用synchronized避免多線程問題 public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
1、解決了線程不安全問題
2、效率低下,每次調用該方法都要涉及鎖的操作。
3、不推薦使用。
問:java設計模式的單例模式,在懶漢式中一開始聲明的類的實例化對象為什么只用private static聲明,但沒有加final關鍵字?而在餓漢式中聲明實例是使用了private static final修飾?
答:如果是final非static成員,必須在構造器、代碼塊、或者直接定義賦值;如果是final static成員變量,必須直接賦值或者在靜態代碼塊中賦值。而在懶漢式中如果直接賦值就達不到延遲加載的效果。
4、實現方式四
該方式是同步代碼塊實現懶漢式。
package com.yefengyu.type4; public class Singleton { private static Singleton instance; //構造器私有化,防止new對象 private Singleton() { } //只有在第一次使用的時候構造實例對象,使用synchronized代碼塊和雙重判斷避免多線程問題,並且提供效率 public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
1、解決了線程不安全問題
2、效率高,只有前面的少數線程可能會獲取鎖,只要實例被創建,后面的線程一般只需第一個if判斷就返回了對象。
3、推薦使用。
5、實現方式五
該方式是靜態內部類實現懶漢式。
package com.yefengyu.type5; public class Singleton { private static Singleton instance; //構造器私有化,防止new對象 private Singleton() { } //靜態內部類,在外部類加載的時候不會加載靜態內部類 private static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { //只有在使用到靜態內部類的時候才會加載,並且通過類加載機制保證在初始化的時候只有一個實例產生 return SingletonInstance.INSTANCE; } }
1、線程安全,使用類加載機制保證初始化時只有一個線程。
2、外部類裝載的時候靜態內部類不會裝載,只有使用的時候才會裝載,因此達成了懶漢式的效果。
單例設計模式的使用場景?
一個對象即可完成所有的工作,無需大量創建對象消耗資源。比如一個長連接,建立起來就可以不斷發送數據,此時如果每來一個請求就建立一個連接,那么資源會消耗殆盡。
多線程測試:針對懶漢式
測試代碼:
for (int i = 0; i < 1000; i++) { new Thread(() -> { Singleton.getInstance(); }).start(); }
在new實例的時候加上打印,如:
public static synchronized Singleton getInstance() { if (instance == null) { System.out.println("創建實例"); instance = new Singleton(); } return instance; }