1、前言單例模式,屬於建立類型的一種經常使用的軟件設計模式。經過單例模式的方法建立的類在當前進程中只有一個實例(根據須要,也有可能一個線程中屬於單例,如:僅線程上下文內使用同一個實例)--來自百度百科數據庫
單例模式可讓咱們只建立一個對象從而避免了頻繁建立對象致使的內存消耗和垃圾回收。設計模式
單例模式只容許建立一個對象,所以節省內存,加快對象訪問速度,所以對象須要被公用的場合適合使用,如多個模塊使用同一個數據源鏈接對象等等。如:安全
項目中的具體應用:多線程
單例模式有八種方式:ide
代碼實現:工具
/** * 單例模式 * @Author: crush * @Date: 2021-08-06 9:14 * version 1.0 */ public class SingletonTest1 { public static void main(String[] args) { // 獲取兩次,看獲取到的對象 確實是單例的嗎 Singleton singleton = Singleton.getInstance(); Singleton singleton1 = Singleton.getInstance(); System.out.println(singleton == singleton1); System.out.println("singleton hashcode:"+singleton.hashCode()); System.out.println("singleton1 hashcode:"+singleton1.hashCode()); /** * 輸出: * true * singleton hashcode:24324022 * singleton1 hashcode:24324022 */ } } /** * 一、餓漢式(靜態常量)代碼實現 */ class Singleton{ /*** 構造器私有化*/ private Singleton(){}; /** * 在類的內部建立一個對象實例 隨當前類加載而加載 沒有線程安全問題。 */ private final static Singleton INSTANCE=new Singleton(); /*** 再提供一個 公有的方法來返回這個靜態常量*/ public static Singleton getInstance(){ return INSTANCE; } }
結論及優缺:測試
優勢:餓漢式(靜態常量方式)它不用擔憂線程安全問題,它自己就是在類的裝載的時候完成實例化的。spa
缺點:可是缺點也在這個地方,無論用不用,只要類裝載了,那麼他就會直接完成實例化,不能達到懶加載的效果,若是你從始至終都沒有使用過這個類,那麼就會形成內存的浪費。線程
注意
:你可能會想類都已經加載了,爲何我還會不用到呢?設計
在單例模式中大都調用getInstance方法,getInstance這個靜態方法可讓類加載,那麼一樣的道理,若是這個類中還有其餘的靜態方法,你調用它的時候,一樣會使類進行裝載。
小結:這種單例方式,其實仍是能夠用的,至少不用擔憂線程同步問題,那種使用特別頻繁的類用這種方式仍是沒啥問題的,除了可能會致使內存浪費,
/** * 單例模式 2 * * @Author: crush * @Date: 2021-08-06 9:14 * version 1.0 */ public class SingletonTest1 { public static void main(String[] args) { // 咱們去拿兩次,看獲取到的對象 確實是單例的嗎 Singleton singleton = Singleton.getInstance(); Singleton singleton1 = Singleton.getInstance(); System.out.println(singleton == singleton1); System.out.println("singleton hashcode:" + singleton.hashCode()); System.out.println("singleton1 hashcode:" + singleton1.hashCode()); /** * 輸出: * true * singleton hashcode:24324022 * singleton1 hashcode:24324022 */ } } /** * 一、餓漢式(靜態代碼塊)代碼實現 */ class Singleton { /** * 構造器私有化 */ private Singleton() { } /** * 在類的內部建立一個對象實例 隨當前類加載而加載 沒有線程安全問題。*/ private static Singleton singleton; static { //改成在靜態代碼塊中 建立單例對象 singleton = new Singleton(); } /** * 再提供一個 公有的方法來返回這個靜態常量 */ public static Singleton getInstance() { return singleton; } }
結論:這種方式其實和第一種很是相似,只是將類的實例化過程放進靜態代碼塊而已。優缺點同餓漢式(靜態常量)。
/** * 單例模式 * * @Author: crush * @Date: 2021-08-06 9:14 * version 1.0 */ public class SingletonTest3 { public static void main(String[] args) { //懶漢式 線程不安全方式,適合單線程使用 //===========單線程下是安全的 ,測試代碼和第一種同樣=========== // =========模擬多線程下============= Runnable runnable = new Runnable(){ @Override public void run() { Singleton instance = Singleton.getInstance(); System.out.println(instance.hashCode()); } }; Runnable runnable2 = new Runnable(){ @Override public void run() { Singleton instance1 = Singleton.getInstance(); System.out.println(instance1.hashCode()); } }; Thread thread1 = new Thread(runnable); Thread thread2 = new Thread(runnable2); thread1.start(); thread2.start(); /** * 結果並不惟一, * 可能會出現相同,也有可能不一樣,多測幾回,就能發現是線程不安全的。 * 94433 * 21648409 */ } } /** * 懶漢式 線程不安全方式 */ class Singleton { /*** 構造器私有化*/ private Singleton() {} /*** 在類的內部建立一個對象實例 隨當前類加載而加載 沒有線程安全問題。*/ private static Singleton singleton; /** * 提供一個公有的方法 * 當使用到這個方法時,纔去建立singleton */ public static Singleton getInstance() { if(singleton==null){ // 經過在這裏堵賽的方式來模擬多線程 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } singleton= new Singleton(); } return singleton; } }
結論及優缺:
if(singleton==null)
下,但還將來的及執行,第二個線程就緊隨而來也進入了if(singleton==null)
下,那麼就會建立多個實例。就不是單例模式了。