您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Java中如何實現單例模式,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
單例模式的特點
一個類只允許產生一個實例化對象。
單例類構造方法私有化,不允許外部創建對象。
單例類向外提供靜態方法,調用方法返回內部創建的實例化對象。
懶漢式(線程不安全)
其主要表現在單例類在外部需要創建實例化對象時再進行實例化,進而達到Lazy Loading 的效果。
通過靜態方法 getSingleton() 和private 權限構造方法為創建一個實例化對象提供唯一的途徑。
不足:未考慮到多線程的情況下可能會存在多個訪問者同時訪問,發生構造出多個對象的問題,所以在多線程下不可用這種方法。
/** * @author MrRoot * @since 2018-12-17 * 懶漢式(線程不安全) */ public class Singleton { private static Singleton singleton; private Singleton(){ } public static Singleton singleton(){ if (singleton == null){ singleton = new Singleton(); } return singleton; } }
懶漢式(線程安全,同步方法,不推薦使用)
針對懶漢式的線程不安全,自然會想到給 getSingleton() 進行 synchronized 加鎖來保證線程同步。
不足:效率低。大多數情況下這個鎖占用的額外資源都浪費了,每個線程在想獲得類的實例時候,執行 getSingleton() 方法都要進行同步。
/** * @author MrRoot * @since 2019-3-27 * 懶漢式(線程安全,同步方法,不推薦使用) */ public class Singleton { private static Singleton singleton; private Singleton(){ } public static synchronized Singleton singleton(){ if (singleton == null){ singleton = new Singleton(); } return singleton; } }
餓漢式(線程安全)
在進行類加載時完成實例化對象的過程就是餓漢式的形式。
避免了線程同步問題,在運行這個類的時候進行加載,之后直接訪問
不足:相比接下來的靜態內部類而言,這種方法比靜態內部類多了內存常駐,容易造成內存浪費,也未達到延遲加載的效果。
/** * @author MrRoot * @since 2019-3-27 * 餓漢式(線程安全) */ public class Singleton{ private static Singleton singleton = new Singleton(); private Singleton(){ } public static Singleton singleton(){ return singleton; } }
靜態內部類加載(線程安全)
靜態內部類不會在單例加載時加載,當調用 getSingleton() 方法時才會進行加載,達到類似懶漢式效果,并且也是線程安全的。
類的靜態屬性只會在第一次加載類時進行初始化,所以上面的方法JVM 幫助我們保證了線程的安全性,在類進行初始化時,其他線程無法進入。
/** * @author MrRoot * @since 2019-3-27 * 靜態內部類加載(線程安全) */ public class Singleton{ private static Singleton singleton; private static class SingletonInner{ private static final Singleton instance = new Singleton(); } public static Singleton getSingleton(){ return SingletonInner.instance; } }
枚舉(線程安全)
自由串行化;保證只有一個實例;線程安全。
Effective Java 作者所提倡的方法,近乎完美,在繼承場景下不適用。
/** * @author MrRoot * @since 2019-3-27 * 枚舉(線程安全) */ enum Singleton{ INSTANCE; public void method(){ } } class Test{ public static void main(String[] args) { Singleton.INSTANCE.method(); } }
懶漢式雙重校驗鎖法(通常線程安全,不可保證完全安全)
使用同步代碼塊避免了第二種方法的效率低的問題,但此方法并不能完全起到線程同步的作用,與上面第一種方法產生的問題相似,多線程訪問時可能產生多個對象。
/** * @author MrRoot * @since 2019-3-27 * 懶漢式雙重校驗鎖法(通常線程安全,不可保證完全安全) */ class Singleton{ private static Singleton singleton; private Singleton(){ } public static Singleton singleton(){ if (singleton == null){ synchronized (Singleton.class){ if (singleton == null){ singleton = new Singleton(); } } } return singleton; } }
懶漢式雙重檢查終極版
與第六種方法不同的是,此方法給singleton 的聲明上加了關鍵字 volatile ,進而解決了低概率的線程不安全問題。
volatile 起到禁止指令重排的作用,在它賦值完成之前,就不會調用讀操作(singleton == null)。
/** * @author MrRoot * @since 2019-3-27 * 懶漢式雙重檢查終極版(volatile) */ class Singleton{ private static volatile Singleton singleton; private Singleton(){ } public static Singleton singleton(){ if (singleton == null){ synchronized (Singleton.class){ if (singleton == null){ singleton = new Singleton(); } } } return singleton; } }
使用 ThreadLocal 實現(線程安全)
ThreadLocal 會為每一個線程提供一個獨立的變量副本,從而隔離了多個線程對數據的訪問沖突。
對于多線程資源共享的問題,同步機制采用了“以時間換空間”的方式,而ThreadLocal 采用了“以空間換時間”的方式。前者僅提供一份變量,讓不同的線程排隊訪問,而后者為每一個線程都提供了一份變量,因此可以同時訪問而互不影響。
/** * @author MrRoot * @since 2019-3-27 * 使用 ThreadLocal 實現(線程安全) */ class Singleton{ private static final ThreadLocal<Singleton> singleton = new ThreadLocal<Singleton>(){ @Override protected Singleton initialValue(){ return new Singleton(); } }; private Singleton(){ } public static Singleton getSingleton(){ return singleton.get(); } }
使用CAS 鎖實現(線程安全)
/** * @author MrRoot * @since 2019-3-27 * 使用 CAS 實現(線程安全) */ public class Singleton { private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>(); private Singleton(){ } public static final Singleton getSingleton(){ for (;;){ Singleton current = INSTANCE.get(); if (current != null){ return current; } current = new Singleton(); if (INSTANCE.compareAndSet(null,current)){ return current; } } } public static void main(String[] args) { Singleton singleton1 = Singleton.getSingleton(); Singleton singleton2 = Singleton.getSingleton(); System.out.println(singleton1 == singleton2); } }
關于Java中如何實現單例模式就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。