<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ## 1. 單例模式介紹 ### 1.1 定義 保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。 ### 1.2 為什么要用單例模式呢? 在我們的系統中,有一些對象其實我們只需要一個,比如說: ***線程池、緩存、對話框、注冊表、日志對象、充當打印機、顯卡*** 等設備驅動程序的對象。事實上,這一類對象只能有一個實例,如果制造出多個實例就可能會導致一些問題的產生,比如:程序的行為異常、資源使用過量、或者不一致性的結果。 **簡單來說使用單例模式可以帶來下面幾個好處:** 1. 對于頻繁使用的對象,可以省略創建對象所花費的時間,這對于那些重量級對象而言,是非常可觀的一筆系統開銷; 2. 由于 new 操作的次數減少,因而對系統內存的使用頻率也會降低,這將減輕 GC 壓力,縮短 GC 停頓時間。 ### 1.3 為什么不使用全局變量確保一個類只有一個實例呢? 我們知道全局變量分為 ***靜態變量*** 和 ***實例變量***,靜態變量也可以保證該類的實例只存在一個。 只要程序加載了類的字節碼,不用創建任何實例對象,靜態變量就會被分配空間,靜態變量就可以被使用了。 但是,如果說這個對象非常消耗資源,而且程序某次的執行中一直沒用,這樣就造成了資源的浪費。利用單例模式的話,我們就可以實現 ***在需要使用時才創建對象***,這樣就避免了不必要的資源浪費。?不僅僅是因為這個原因,在程序中我們要盡量避免全局變量的使用,大量使用全局變量給程序的調試、維護等帶來困難。 ***** ## 2 單例的模式的實現 通常單例模式在Java語言中,有兩種構建方式: * 餓漢方式。指全局的單例實例在類裝載時構建 * 懶漢方式。指全局的單例實例在第一次被使用時構建。 不管是那種創建方式,它們通常都存在下面幾點相似處: * 單例類必須要有一個 private 訪問級別的構造函數,只有這樣,才能確保單例不會在系統中的其他代碼內被實例化; * instance 成員變量和 uniqueInstance 方法必須是 static 的。 ### 2.1 餓漢方式(線程安全) ``` public class Singleton { //在靜態初始化器中創建單例實例,這段代碼保證了線程安全 private static Singleton uniqueInstance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return uniqueInstance; } } ``` 所謂?“餓漢方式”?就是說JVM在加載這個類時就馬上創建此唯一的單例實例,不管你用不用,先創建了再說,如果一直沒有被使用,便浪費了空間,典型的空間換時間,每次調用的時候,就不需要再判斷,節省了運行時間。 ### 2.2 懶漢式(非線程安全和synchronized關鍵字線程安全版本 ) ``` public class Singleton { private static Singleton uniqueInstance; private Singleton (){ } //沒有加入synchronized關鍵字的版本是線程不安全的 public static Singleton getInstance() { //判斷當前單例是否已經存在,若存在則返回,不存在則再建立單例 if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } } ``` 所謂?“餓漢方式”?就是說單例實例在第一次被使用時構建,而不是在JVM在加載這個類時就馬上創建此唯一的單例實例。 但是上面這種方式很明顯是線程不安全的,如果多個線程同時訪問getInstance()方法時就會出現問題。如果想要保證線程安全,一種比較常見的方式就是在getInstance() 方法前加上synchronized關鍵字,如下: ``` public static synchronized Singleton getInstance() { if (instance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } ``` 我們知道synchronized關鍵字偏重量級鎖。雖然在JavaSE1.6之后synchronized關鍵字進行了主要包括:為了減少獲得鎖和釋放鎖帶來的性能消耗而引入的偏向鎖和輕量級鎖以及其它各種優化之后執行效率有了顯著提升。 但是在程序中每次使用getInstance() 都要經過synchronized加鎖這一層,這難免會增加getInstance()的方法的時間消費,而且還可能會發生阻塞。我們下面介紹到的?雙重檢查加鎖版本?就是為了解決這個問題而存在的。 ### 2.3 懶漢式(雙重檢查加鎖版本) 利用雙重檢查加鎖(double-checked locking),首先檢查是否實例已經創建,如果尚未創建,“才”進行同步。這樣以來,只有一次同步,這正是我們想要的效果。 ``` public class Singleton { //volatile保證,當uniqueInstance變量被初始化成Singleton實例時, //多個線程可以正確處理uniqueInstance變量 private volatile static Singleton uniqueInstance; private Singleton() { } public static Singleton getInstance() { //檢查實例,如果不存在,就進入同步代碼塊 if (uniqueInstance == null) { //只有第一次才徹底執行這里的代碼 synchronized(Singleton.class) { //進入同步代碼塊后,再檢查一次,如果仍是null,才創建實例 if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } } return uniqueInstance; } } ``` 很明顯,這種方式相比于使用synchronized關鍵字的方法,可以大大減少getInstance() 的時間消費。 我們上面使用到了volatile關鍵字來保證數據的可見性,uniqueInstance 采用 volatile 關鍵字修飾也是很有必要。 uniqueInstance 采用 volatile 關鍵字修飾也是很有必要的, uniqueInstance = new Singleton(); 這段代碼其實是分為三步執行: 1. 為 uniqueInstance 分配內存空間 2. 初始化 uniqueInstance 3. 將 uniqueInstance 指向分配的內存地址 但是由于 JVM 具有指令重排的特性,執行順序有可能變成 1->3->2。 指令重排 在單線程環境下不會出現問題,但是在多線程環境下會導致一個線程獲得還沒有 初始化的實例。例如,線程 T1 執行了 1 和 3,此時 T2 調用 getUniqueInstance() 后發現 uniqueInstance 不為空,因此返回 uniqueInstance,但此時 uniqueInstance 還未被初始化。 使用 volatile 可以禁止 JVM 的指令重排,保證在多線程環境下也能正常運行。 ### 2.4 懶漢式(登記式/靜態內部類方式) 靜態內部實現的單例是懶加載的且線程安全。 只有通過顯式調用 getInstance 方法時,才會顯式裝載 SingletonHolder 類,從而實例化 instance(只有第一次使用這個單例的實例的時候才加載,同時不會有線程安全問題)。 ``` public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } } ``` ### 2.5 餓漢式(枚舉方式) 這種實現方式還沒有被廣泛采用,但這是實現單例模式的最佳方法。它更簡潔,自動支持序列化機制,絕對防止多次實例化,同時這種方式也是《Effective Java 》以及《Java與模式》的作者推薦的方式。 ``` public enum Singleton { //定義一個枚舉的元素,它就是 Singleton 的一個實例 INSTANCE; public void doSomeThing() { System.out.println("枚舉方法實現單例"); } } ``` 使用方法: ``` public class ESTest { public static void main(String[] args) { Singleton singleton = Singleton.INSTANCE; singleton.doSomeThing();//output:枚舉方法實現單例 } } ``` 《Effective Java 中文版 第二版》 這種方法在功能上與公有域方法相近,但是它更加簡潔,無償提供了序列化機制,絕對防止多次實例化,即使是在面對復雜序列化或者反射攻擊的時候。雖然這種方法還沒有廣泛采用,但是單元素的枚舉類型已經成為實現Singleton的最佳方法。 —-《Effective Java 中文版 第二版》 《Java與模式》 《Java與模式》中,作者這樣寫道,使用枚舉來實現單實例控制會更加簡潔,而且無償地提供了序列化機制,并由JVM從根本上提供保障,絕對防止多次實例化,是更簡潔、高效、安全的實現單例的方式。 ### 2.6 實例場景之 - 獲取jdbc連接 我們新增兩個類,第一個是非單例的獲取jdbc連接 ``` package com.mk.designDemo.singleton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class MDataSourceNoSingle { private static final Logger logger = LoggerFactory.getLogger(MDataSourceNoSingle.class); private static Connection connection = null; public Connection getConnection() { try { Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection("jdbc:mysql://******:3306/*****?characterEncoding=UTF-8", "root", "root"); logger.info("線程{}實例化connection", Thread.currentThread().getName()); } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } return connection; } } ``` 第二個使用了單例模式 ``` package com.mk.designDemo.singleton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class MDataSourceSingle { private static final Logger logger = LoggerFactory.getLogger(MDataSourceSingle.class); private static Connection connection = null; static { try { Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection("jdbc:mysql://******:3306/*****?characterEncoding=UTF-8", "root", "root"); logger.info("線程{}實例化connection", Thread.currentThread().getName()); } catch (ClassNotFoundException | SQLException e) { logger.error(e.getMessage(), e); } } private MDataSourceSingle() { } public static Connection getConnection() { return connection; } } ``` 然后我們寫了一個簡單的controller,里面有兩個接口,用來驗證結果 ``` package com.mk.designDemo.controller; import com.mk.designDemo.singleton.MDataSourceNoSingle; import com.mk.designDemo.singleton.MDataSourceSingle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.sql.Connection; @RestController public class SingletonController { private static final Logger logger = LoggerFactory.getLogger(SingletonController.class); private final static String sql = "select * from t_trade_date limit 10"; @RequestMapping(path = "/noSingleton") public String noSingleton() { MDataSourceNoSingle mDataSourceNoSingle = new MDataSourceNoSingle(); Connection connection = mDataSourceNoSingle.getConnection(); doExecute(connection, sql); return "OK"; } @RequestMapping(path = "/singleton") public String singleton() { doExecute(MDataSourceSingle.getConnection(), sql); return "OK"; } private void doExecute(Connection connection, String sql) { logger.info("do execute sql:{}", sql); } } ``` 當我們調用三次非單例的接口時,查看日志輸出如下,connection被實例化了多次: ``` 2019-07-09 15:47:31.966 [http-nio-80-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/noSingleton", parameters={} 2019-07-09 15:47:31.970 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.noSingleton() 2019-07-09 15:47:32.633 [http-nio-80-exec-1] INFO c.m.d.singleton.MDataSourceNoSingle - 線程http-nio-80-exec-1實例化connection 2019-07-09 15:47:32.633 [http-nio-80-exec-1] INFO c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10 2019-07-09 15:47:32.671 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json] 2019-07-09 15:47:32.672 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"] 2019-07-09 15:47:32.693 [http-nio-80-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK 2019-07-09 15:47:40.005 [http-nio-80-exec-4] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/noSingleton", parameters={} 2019-07-09 15:47:40.007 [http-nio-80-exec-4] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.noSingleton() 2019-07-09 15:47:40.361 [http-nio-80-exec-4] INFO c.m.d.singleton.MDataSourceNoSingle - 線程http-nio-80-exec-4實例化connection 2019-07-09 15:47:40.361 [http-nio-80-exec-4] INFO c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10 2019-07-09 15:47:40.362 [http-nio-80-exec-4] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json] 2019-07-09 15:47:40.362 [http-nio-80-exec-4] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"] 2019-07-09 15:47:40.363 [http-nio-80-exec-4] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK 2019-07-09 15:47:48.459 [http-nio-80-exec-7] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/noSingleton", parameters={} 2019-07-09 15:47:48.461 [http-nio-80-exec-7] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.noSingleton() 2019-07-09 15:47:48.801 [http-nio-80-exec-7] INFO c.m.d.singleton.MDataSourceNoSingle - 線程http-nio-80-exec-7實例化connection 2019-07-09 15:47:48.801 [http-nio-80-exec-7] INFO c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10 2019-07-09 15:47:48.802 [http-nio-80-exec-7] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json] 2019-07-09 15:47:48.802 [http-nio-80-exec-7] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"] 2019-07-09 15:47:48.803 [http-nio-80-exec-7] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK ``` 然后我們調用單例的接口三次,日志輸出如下,我們可以看到,只有第一次進行了實例化,后面再也沒有繼續實例化 ``` 2019-07-09 15:49:22.868 [http-nio-80-exec-10] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/singleton", parameters={} 2019-07-09 15:49:22.871 [http-nio-80-exec-10] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.singleton() 2019-07-09 15:49:23.234 [http-nio-80-exec-10] INFO c.m.d.singleton.MDataSourceSingle - 線程http-nio-80-exec-10實例化connection 2019-07-09 15:49:23.235 [http-nio-80-exec-10] INFO c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10 2019-07-09 15:49:23.237 [http-nio-80-exec-10] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json] 2019-07-09 15:49:23.237 [http-nio-80-exec-10] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"] 2019-07-09 15:49:23.239 [http-nio-80-exec-10] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK 2019-07-09 15:49:25.032 [http-nio-80-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/singleton", parameters={} 2019-07-09 15:49:25.034 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.singleton() 2019-07-09 15:49:25.034 [http-nio-80-exec-1] INFO c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10 2019-07-09 15:49:25.035 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json] 2019-07-09 15:49:25.036 [http-nio-80-exec-1] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"] 2019-07-09 15:49:25.038 [http-nio-80-exec-1] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK 2019-07-09 15:49:25.926 [http-nio-80-exec-2] DEBUG o.s.web.servlet.DispatcherServlet - GET "/web/singleton", parameters={} 2019-07-09 15:49:25.926 [http-nio-80-exec-2] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.mk.designDemo.controller.SingletonController.singleton() 2019-07-09 15:49:25.927 [http-nio-80-exec-2] INFO c.m.d.controller.SingletonController - do execute sql:select * from t_trade_date limit 10 2019-07-09 15:49:25.928 [http-nio-80-exec-2] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json] 2019-07-09 15:49:25.928 [http-nio-80-exec-2] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["OK"] 2019-07-09 15:49:25.929 [http-nio-80-exec-2] DEBUG o.s.web.servlet.DispatcherServlet - Completed 200 OK ``` 驗證通過,OVER! ### 2.7 總結 我們主要介紹到了以下幾種方式實現單例模式: * 餓漢方式(線程安全) * 懶漢式(非線程安全和synchronized關鍵字線程安全版本) * 懶漢式(雙重檢查加鎖版本) * 懶漢式(登記式/靜態內部類方式) * 餓漢式(枚舉方式) 參考: 《Head First 設計模式》 《Effective Java 中文版 第二版》 [【Java】設計模式:深入理解單例模式](https://itimetraveler.github.io/2016/09/08/%E3%80%90Java%E3%80%91%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%9A%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/#6、枚舉)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看