Java併發程式設計中的設計模式解析(二)一個單例的七種寫法
Java單例模式是最常見的設計模式之一,廣泛應用於各種框架、中介軟體和應用開發中。單例模式實現起來比較簡單,基本是每個Java工程師都能信手拈來的,本文將結合多執行緒、類的載入等知識,系統地介紹一下單例模式的演變,並體現在7種不同的單例設計中。說到這個,非常像孔乙己裡那個“回字有四種寫法”的梗,不過與封建迂腐文人不同的是,從簡單的單例設計變化,可以看到一個需求演變的過程,看到一個方法不斷完善的過程。
1. 餓漢式
最簡單的單例設計,優點是執行緒安全,但是因為類載入即初始化例項,加入例項變數比較多的話,會佔用較多的記憶體。
1 //不允許被繼承 2 public final class SingletonStarve { 3 //例項變數, 由於單例物件是靜態的, 在類的載入階段, 就會初始化例項變數 4 @SuppressWarnings("unused") 5 private byte[] data = new byte[1024]; 6 //定義靜態例項物件的時候直接初始化 7 private static SingletonStarve instance = new SingletonStarve(); 8 //私有化建構函式, 不允許直接new物件 9 private SingletonStarve() {} 10 //提供公共的方法獲取例項物件 11 public static SingletonStarve getInstance() { 12 return instance; 13 } 14 }
2. 懶漢式
實現了單例設計的懶載入,節省了前期記憶體空間的佔用,但是在多執行緒環境下可能會導致多物件的產生,破壞例項唯一性。
1 //不允許被繼承 2 public final class LazySingleton { 3 //例項變數, 由於單例物件是靜態的, 在類的載入階段, 就會初始化例項變數 4 @SuppressWarnings("unused") 5 private byte[] data = new byte[1024]; 6 //定義靜態例項物件, 不直接初始化 7 private static LazySingleton instance = null; 8 //私有化建構函式, 不允許直接new物件 9 private LazySingleton() {} 10 //提供公共的方法獲取例項物件 11 public static LazySingleton getInstance() { 12 if(null == instance) { 13 instance = new LazySingleton(); 14 } 15 return instance; 16 } 17 }
3. 懶漢式+同步鎖
通過使用synchronized關鍵字使getInstance方法變為同步方法,從而確保執行緒安全,但帶來了一定的效能問題。
1 //不允許被繼承 2 public final class SyncLazySingleton { 3 //例項變數, 由於單例物件是靜態的, 在類的載入階段, 就會初始化例項變數 4 @SuppressWarnings("unused") 5 private byte[] data = new byte[1024]; 6 //定義靜態例項物件, 不直接初始化 7 private static SyncLazySingleton instance = null; 8 //私有化建構函式, 不允許直接new物件 9 private SyncLazySingleton() {} 10 //提供公共的方法獲取例項物件, 通過synchronized修飾為同步方法 11 public static synchronized SyncLazySingleton getInstance() { 12 if(null == instance) { 13 instance = new SyncLazySingleton(); 14 } 15 return instance; 16 } 17 }
4. Double-Check
推薦使用:Double-Check單例模式,通過兩次非空判斷,並且對第二次判斷加鎖,確保了多執行緒下的單例設計安全,同時保證了效能。
注意:Double-check有可能因為JVM指令重排的原因,導致空指標異常;使用volatile修飾物件引用,可以確保其可見性,避免異常
1 //不允許被繼承 2 public final class VolatileDoubleCheckSingleton { 3 //例項變數, 由於單例物件是靜態的, 在類的載入階段, 就會初始化例項變數 4 @SuppressWarnings("unused") 5 private byte[] data = new byte[1024]; 6 //定義靜態例項物件, 不直接初始化 7 //通過volatile, 避免指令重排序導致的空指標異常 8 private static volatile VolatileDoubleCheckSingleton instance = null; 9 Connection conn; 10 Socket socket; 11 //私有化建構函式, 不允許直接new物件 12 //由於指令重排序, 例項化順序可能重排, 從而導致空指標,使用volatile關鍵字修飾單例解決 13 private VolatileDoubleCheckSingleton() { 14 //this.conn; 15 //this.socket; 16 } 17 //提供公共的方法獲取例項物件 18 public static VolatileDoubleCheckSingleton getInstance() { 19 20 if(null == instance) { 21 synchronized(VolatileDoubleCheckSingleton.class) { 22 if(null == instance) {//以下賦值因為不是原子性的,如果不使用volatile使instance在多個執行緒中可見,將可能導致空指標 23 instance = new VolatileDoubleCheckSingleton(); 24 } 25 } 26 } 27 return instance; 28 } 29 }
5. 靜態內部類
推薦使用:通過使用靜態內部類,巧妙地避免了執行緒不安全,並且節省了前期記憶體空間,編碼非常簡潔。
1 //不允許被繼承 2 public final class HolderSingleton { 3 //例項變數 4 @SuppressWarnings("unused") 5 private byte[] data = new byte[1024]; 6 //私有化構造器 7 private HolderSingleton() {} 8 //定義靜態內部類Holder, 及內部例項成員, 並直接初始化 9 private static class Holder{ 10 private static HolderSingleton instance = new HolderSingleton(); 11 } 12 //通過Holder.instance獲得單例 13 public static HolderSingleton getInstance() { 14 return Holder.instance; 15 } 16 }
6. 列舉類
《Effective Java》中推薦的單例設計模式,缺點是餓漢式,並且對編碼能力要求較高。
1 //列舉本身是final的, 不允許被繼承 2 public enum EnumSingleton { 3 INSTANCE; 4 //例項變數 5 @SuppressWarnings("unused") 6 private byte[] data = new byte[1024]; 7 8 EnumSingleton() { 9 System.out.println("INSTANCE will be initialized immediately"); 10 } 11 public static void method() { 12 //呼叫該方法會主動使用EnumSingleton, INSTANCE將會例項化 13 } 14 public static EnumSingleton getInstance() { 15 return INSTANCE; 16 } 17 }
7. 內部列舉類
1 /* 2 * 使用列舉類作為內部類實現懶載入 3 */ 4 public final class LazyEnumSingleton { 5 private LazyEnumSingleton(){} 6 private enum EnumHolder{ 7 INSTANCE; 8 private LazyEnumSingleton instance; 9 EnumHolder(){ 10 this.instance = new LazyEnumSingleton(); 11 } 12 private LazyEnumSingleton getLazyEnumSingleton() { 13 return instance; 14 } 15 } 16 public static LazyEnumSingleton getInstance() { 17 return EnumHolder.INSTANCE.getLazyEnumSingleton(); 18 } 19 }
相關文章
- Java併發程式設計中的設計模式解析(一)Java程式設計設計模式
- 設計模式 - 單例模式Singleton的8種寫法設計模式單例
- Java:單例模式的七種寫法Java單例模式
- Java設計模式——實現單例模式的七種方式[JZOF]Java設計模式單例
- java23種設計模式—— 二、單例模式Java設計模式單例
- 單例模式的七種寫法單例模式
- 【本人禿頂程式設計師】最簡單的設計模式——單例模式的演進和推薦寫法(Java 版)程式設計師設計模式單例Java
- Java 併發程式設計解析Java程式設計
- Java併發程式設計---java規範與模式下的併發程式設計1.1Java程式設計模式
- 設計模式(二)——單例模式設計模式單例
- java設計模式其一 單例模式Java設計模式單例
- Java設計模式【單例模式】Java設計模式單例
- Java設計模式 | 單例模式Java設計模式單例
- Java設計模式——單例模式Java設計模式單例
- Java設計模式–單例模式Java設計模式單例
- Java設計模式-單例模式Java設計模式單例
- Java設計模式--單例模式Java設計模式單例
- java單例設計模式Java單例設計模式
- Java設計模式系列之單例設計模式Java設計模式單例
- 淺識JAVA設計模式——單例設計模式Java設計模式單例
- [java設計模式]單例設計模式,程式也要進行計劃生育。Java設計模式單例
- 一天一個設計模式(二) - 單例模式(Singleton)設計模式單例
- jS設計模式二:單例模式JS設計模式單例
- JS設計模式(二)--- 單例模式JS設計模式單例
- 單例模式的七種寫法,你都知道嗎?單例模式
- 淺談23種設計模式之單例設計模式設計模式單例
- Java常用設計模式-單例模式Java設計模式單例
- Java設計模式之單例模式Java設計模式單例
- java設計模式-單例模式SingletonJava設計模式單例
- 設計模式(一)_單例模式設計模式單例
- 設計模式一(單例模式)設計模式單例
- Kotlin設計模式解析之單例Kotlin設計模式單例
- 一個故事搞懂Java併發程式設計Java程式設計
- JAVA設計模式 1 設計模式介紹、單例模式的理解與使用Java設計模式單例
- [java設計模式]破解單例設計模式,禁止偷生、超生!Java設計模式單例
- 《java學習二》併發程式設計Java程式設計
- 單例模式(下)---聊一聊單例模式的幾種寫法單例模式
- 單例模式(下) - 聊一聊單例模式的幾種寫法單例模式