1. 程式人生 > >設計模式—singleton(單例模式)

設計模式—singleton(單例模式)

**單例模式** * 單例設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。這種模式涉及到一個單一的類,該類負責建立自己的物件,同時確保只有單個物件被建立。 這個類提供了一種訪問其唯一的物件的方式,可以直接訪問,不需要例項化該類的物件。 **應用例項** * Windows是多程序多執行緒的,通過唯一的例項來操作一個檔案,避免地出現多個程序或執行緒同時操作一個檔案的現象。 * 一些裝置管理器常常設計為單例模式(如一個電腦有兩臺印表機,在輸出的時候就要處理不能兩臺印表機列印同一個檔案)。 **使用場景** * 要求生產唯一序列號。 * WEB 中的計數器,不用每次重新整理都在資料庫里加一次,用單例先快取起來。 * 建立的一個物件需要消耗的資源過多,比如 I/O 與資料庫的連線等。 **單例實現** * 方式一 ![](https://img2020.cnblogs.com/blog/2282070/202103/2282070-20210322115250808-867812575.png) **方式二** * 改進了方式一的缺點,但同時帶來了執行緒安全問題 * 假設有兩個執行緒,執行緒1到第9行判斷完例項是否為空時(還沒到new一個例項),執行緒2也到了判斷例項是否為空的位置, 因為執行緒1還沒建立例項,INSTANCE仍為空,執行緒2建立完一個例項後,執行緒1繼續執行也建立了一個例項。 ![](https://img2020.cnblogs.com/blog/2282070/202103/2282070-20210322172430367-1939051568.png) **方式三** * 通過synchronized加鎖,解決方式二的執行緒安全問題,但也帶來了效率下降。 * 通過加鎖,鎖定了Mgr03.class物件,因此輸出的hashcode是相同的。 ![](https://img2020.cnblogs.com/blog/2282070/202103/2282070-20210323201308506-2037982314.png) **方式四** * 雙重校驗鎖,完美的單例模式寫法之一。 * 如果只有外層判斷,就會出現執行緒安全問題(如同方式二)。 ![](https://img2020.cnblogs.com/blog/2282070/202103/2282070-20210323203033410-406511331.png) **方式五** * 靜態內部類方式,解決了方式一的問題,達到了按需初始化的目的,是完美的單例模式之一。 ![](https://img2020.cnblogs.com/blog/2282070/202103/2282070-20210323203738300-1495577083.png) **方式六** * 通過列舉實現單例 ![](https://img2020.cnblogs.com/blog/2282070/202103/2282070-20210323204330719-741625910.png) * 列舉方式 [https://blog.csdn.net/moakun/article/details/80688851]() ``` public class Mgr05 { private Mgr05() {}; //類Mgr05載入時,內部類不會載入 private static class Mgr05Holder{ private final static Mgr05 INSTANCE=new Mgr05(); } //呼叫getInstance(),類Mgr05Holder載入 public static Mgr05 getInstance() {return Mgr05Holder.INSTANCE;}; public static void main(String[] args) { for(int i=0;i<200;i++) { new Thread(()->{ System.out.println(Mgr05.getInstance().hashCode()); }).start(); } } } ``` ``` public class Mgr04 { private static volatile Mgr04 INSTANCE; private Mgr04() {}; public static synchronized Mgr04 getInstance() { if(INSTANCE==null) {//雙重校驗鎖 synchronized (Mgr04.class) { if(INSTANCE==null) { try { Thread.sleep(10); }catch(InterruptedException e) { e.printStackTrace(); } } } INSTANCE=new Mgr04(); } return INSTANCE; } public void c() {System.out.println("c");} public static void main(String[] args) { for(int i=0;i<200;i++) { new Thread(()->{ System.out.println(Mgr04.getInstance().hashCode()); }).start(); } }