單例模式是咱們比較經常使用的設計模式,玩好單例模式也會涉及到不少java基礎知識。 單例做爲全局性實例,在多線程狀況下全局共享的變量會變得很是危險。java
雙重檢測是比較經常使用的一種實現方式:設計模式
public class Singleton { public static final volatile Singleton singleton = null; private Singleton(){} public static Singleton getInstance(){ if(singleton == null){ synchronize (Singleton.class){ if( singleton == null ) { singleton = new Singleton(); } } return singleton; } }
若是不用volatile修飾,多線程執行到 singleton == null 時,多個實例會被建立出來,就可能形成內存泄露問題。多線程
固然你能夠說能夠用互斥同步的方式進行,可是咱們作了同步,多線程的操做就變成了串型了,效率會很低,由於建立對象其實只須要一次,可是後面的讀取都須要同步了。jvm
還有一個緣由,在jvm編譯器可能會對指令進行重拍和優化,就是判斷singleton == null的判斷順序可能沒法保證。 因而咱們將變量用volatile修飾,這個變量就不會在多線程中存在副本,都必須從主內存讀取,同時避免了指令重拍。優化
當兩個線程執行完第一個 singleton == null 後等待鎖, 其中一個線程得到鎖並進入synchronize後,實例化了,而後退出釋放鎖,另一個線程得到鎖,進入又想實例化,會判斷是否進行實例化了,若是存在,就不進行實例化了。線程
一個延遲實例化的內部類的單例模式,一個內部類的容器,調用getInstance時,JVM加載這個類設計
public final class Singleton { private static class SingletonHolder { static final Singleton INSTANCE = new Singleton(); } private Singleton() {} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
因爲SingleHolder是私有的,除了getInstance()以外沒有方法能夠訪問它,只有在getInstance()被調用時纔會真正建立,code
首先,其餘類在引用這個Singleton的類時,只是新建了一個引用,並無開闢一個的堆空間存放(對象所在的內存空間)。 接着,當使用Singleton.getInstance()方法後,Java虛擬機(JVM)會加載SingletonHolder.class(JLS規定每一個class對象只能被初始化一次),並實例化一個Singleton對象。對象
缺點:內存
須要在Java的另一個內存空間(Java PermGen 永久代內存,這塊內存是虛擬機加載class文件存放的位置)佔用一個大塊的空間。
更多內容關注: