設計模式-單例模式
阿新 • • 發佈:2020-07-14
單例模式模式一般大家都會認為它比較簡單,其實並非你們所認為的那樣,很多情況下,單例模式會涉及到很多優化,下面給大家簡單介紹一下單例模式的幾種演變過程:
- 餓漢模式
- 懶漢模式
- 懶漢模式(加鎖關鍵字 synchronized)
- 懶漢模式(細粒度的新增synchronized)
- 懶漢模式(雙重檢查)
- 靜態內部類
- 列舉類
第一種:餓漢模式 比較簡單,類載入到記憶體的時候就進行例項化,推薦使用 ,但是有人會說,既然不用幹嘛要進行例項化;
package com.dongl.singleton; /** * 餓漢模式 * 類載入到記憶體就直接例項化一個單例,JVM會保證它的執行緒安全 * 簡單使用 推薦使用 * 缺點:無論用到與否 類載入就會直接例項化 * 有人就會吹毛求疵說:你不用 你例項化幹嘛?*/ public class T01_Singleton { private static T01_Singleton INSTANCE = new T01_Singleton(); public T01_Singleton() { } public static T01_Singleton getInstance(){ return INSTANCE; } public static void main(String[] args) { T01_Singleton t1 = T01_Singleton.getInstance(); T01_Singleton t2= T01_Singleton.getInstance(); System.out.println(t1 == t2); } }
第二種:懶漢模式 這種模式在多執行緒的情況下會出現問題
package com.dongl.singleton; /** * 懶漢模式 lazy loading * 雖然達到了按需初始化的目的 但是也帶來了執行緒安全的問題 * 在多執行緒的情況下 */ public class T02_Singleton { private static T02_Singleton INSTANCE = null; public T02_Singleton() { }public static T02_Singleton getInstance(){ if(INSTANCE == null){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new T02_Singleton(); } return INSTANCE; } public static void main(String[] args) { // T02_Singleton t1 = T02_Singleton.getInstance(); // T02_Singleton t2 = T02_Singleton.getInstance(); // System.out.println(t1 == t2); /**建立100個執行緒 呼叫getInstance() 列印返回的物件的hashcode()*/ for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(T02_Singleton.getInstance().hashCode()); }).start(); } } }
//執行結果 hashcode值不相同
E:\JDK\jdk1.8\bin\java.exe "-javaagent:E:\Idea\IntelliJ IDEA
1365279759
1365279759
1365279759
1945255694
226994615
第三種:懶漢模式(加鎖關鍵字 synchronized)這種方式解決了懶漢模式下多執行緒問題,但是同時帶來的問題是效率降低;
package com.dongl.singleton; /** * 懶漢模式 lazy loading * 雖然達到了按需初始化的目的 但是也帶來了執行緒安全的問題 * 解決辦法使用synchronized 但是帶來的問題就是效率下降 */ public class T03_Singleton { private static T03_Singleton INSTANCE = null; public T03_Singleton() { } /** * 加鎖的方式有兩種 一種是對方法進行加鎖 另一種是對程式碼塊進行加鎖 * 涉及到的無非是鎖的粒度問題 * @return */ public static /**synchronized*/ T03_Singleton getInstance(){ synchronized (T02_Singleton.class) { if (INSTANCE == null) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new T03_Singleton(); } } return INSTANCE; } public static void main(String[] args) { /**建立100個執行緒 呼叫getInstance() 列印返回的物件的hashcode()*/ for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(T03_Singleton.getInstance().hashCode()); }).start(); } } }
第四種:懶漢模式(細粒度的新增synchronized)試圖通過減小鎖的粒度來進行改善效率的問題 但是不可行;
package com.dongl.singleton; /** * 懶漢模式 lazy loading * 雖然達到了按需初始化的目的 但是也帶來了執行緒安全的問題 * 解決辦法使用synchronized 但是帶來的問題就是效率下降 * * 試圖通過減小鎖的粒度來進行改善效率的問題 但是不可行 */ public class T04_Singleton { private static T04_Singleton INSTANCE = null; public T04_Singleton() { } public static /**synchronized*/ T04_Singleton getInstance(){ if (INSTANCE == null) { //試圖通過減小鎖的粒度來進行改善效率的問題 但是不可行 synchronized (T02_Singleton.class) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new T04_Singleton(); } } return INSTANCE; } public static void main(String[] args) { /**建立100個執行緒 呼叫getInstance() 列印返回的物件的hashcode()*/ for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(T04_Singleton.getInstance().hashCode()); }).start(); } } }
第五種:懶漢模式(雙重檢查)這種方式解決了多執行緒帶來的問題;
package com.dongl.singleton; /** * 懶漢模式 lazy loading * 雖然達到了按需初始化的目的 但是也帶來了執行緒安全的問題 * 解決辦法使用synchronized 但是帶來的問題就是效率下降 * 因為鎖的粒度很小也會帶來多執行緒問題 * 這時可以使用雙重檢查 來避免 */ public class T05_Singleton { private volatile static T05_Singleton INSTANCE = null; public T05_Singleton() { } public static /**synchronized*/ T05_Singleton getInstance(){ if (INSTANCE == null) { synchronized (T02_Singleton.class) { //雙重檢查 if(INSTANCE == null) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new T05_Singleton(); } } } return INSTANCE; } public static void main(String[] args) { /**建立100個執行緒 呼叫getInstance() 列印返回的物件的hashcode()*/ for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(T05_Singleton.getInstance().hashCode()); }).start(); } } }
第六種:靜態內部類實現懶載入(lazy loading)這種方式是最優解之一 因為在載入類的時候 不載入內部類這樣就實現了懶載入;
package com.dongl.singleton; /** * 靜態內部類的法方法 * JVM保證單例 * 載入外部類的時候 不會載入內部類 這樣實現了懶載入 */ public class T06_Singleton { public T06_Singleton() { } //靜態內部類 private static class T06_SingletonHandler{ private final static T06_Singleton INSTANCE = new T06_Singleton(); } public static T06_Singleton getInstance(){ T06_Singleton instance = T06_SingletonHandler.INSTANCE; return instance; } public static void main(String[] args) { /**建立100個執行緒 呼叫getInstance() 列印返回的物件的hashcode()*/ for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(T06_Singleton.getInstance().hashCode()); }).start(); } } }
第七種:列舉類不僅可以解決執行緒同步,還可以防止反序列化。
package com.dongl.singleton; /** * 不僅可以解決執行緒同步,還可以防止反序列化。 */ public enum T07_Singleton { INSTANCE; public static void main(String[] args) { /**建立100個執行緒 呼叫getInstance() 列印返回的物件的hashcode()*/ for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(T07_Singleton.INSTANCE.hashCode()); }).start(); } } }
以上其中方式是按照問題出現 一步步的優化得到的,如果你覺得有疑問可以評論區說出你的觀點,一起討論一起進步!!!