1、單例模式的簡介
定義
保證每個類僅有一個實例,並給外部提供一個訪問它的全局訪問點。
思路
如果一個類能夠被創建多個實例,那么,這個類的構造方法肯定是公開的,外部通過此類的構造方法可以創建多個類的實例。只要類的構造方法能讓外部訪問到,我們就沒法控制類的實例的個數。
如果我們把創建類的實例的權限收回來,讓類自身負責創建實例,然后由類本身來提供外部訪問這個類的實例的方法,就實現了單例模式。
在Java中,單例模式的實現分為兩種,一種是懶漢式,另一種是餓漢式。區別在於具體創建對象實例的處理上,有不同的方式。
懶漢式
/** * 懶漢式 * @author Bean_bag */
public class Singleton { //存儲創建好的實例對象
private static Singleton uniqueInstance = null; //私有化構造方法
private Singleton(){} //為外部提供類實例
public static synchronized Singleton getInstance(){ //判斷變量是否有值
if (uniqueInstance == null){ //沒有值,就創建對象並賦值
uniqueInstance = new Singleton(); } //有值就直接使用
return uniqueInstance; } }
餓漢式
/** * 餓漢式 * @author Bean_bag */
public class Singleton { private static Singleton uniqueInstance = new Singleton(); //私有化構造方法
private Singleton(){} //定義一個方法來為客戶端提供類實例
public static Singleton getInstance(){ return uniqueInstance; } }
2、單例模式的詳解
功能
保證類在運行期間只會創建一個實例,並提供了一個全局唯一的訪問這個類的訪問點,就是代碼中的getInstance()方法。不管懶漢式還是餓漢式,這個訪問點是一樣的。對單例模式本身而言,它只關心類實例的創建問題,並不關心具體的業務問題。
范圍
目前Java里面實現的單例是一個ClassLoader及其子ClassLoader的范圍,因為一個ClassLoader在裝載餓漢式單例的時候,就會創建一個類的實例。如果一個虛擬機里面有多個ClassLoader,而這些ClassLoader都裝載某個類的話,就算這個類是單例類,也會產生很多個實例。如果一個機器上有多個虛擬機,那么每個虛擬機里都應該至少有一個這個類的實例,整個機器上有多個實例,就不再是單例了。
關於ClassLoader的更多介紹
https://zhuanlan.zhihu.com/p/51374915
優缺點
懶漢式是典型的時間換空間。每次獲取實例都會進行判斷,判斷是否需要創建實例,浪費判斷的時間。如果沒人使用,就不會創建實例,節省內存空間。
餓漢式是典型的時間換空間,不管是否使用,都創建出來,每次調用的時候,就不再去判斷,節省了運行時間。
線程安全
不加同步的懶漢式是線程不安全的
餓漢式是線程安全的,因為虛擬機保證了只會裝載一次,在裝載類的時候是不會發生並發的。
3、Java中一種更好的單例實現方式
public class Singleton { /** * 類級的內部類,也就是靜態的成員式內部類,該內部類的實例與外部類的實例 * 沒有綁定關系,而且只有被調用到才會裝載,從而實現了延遲加載 */
private static class SingleHolder{ private static Singleton instance = new Singleton(); } /** * 私有化構造方法 */
private Singleton(){} public static Singleton getInstance(){ return SingleHolder.instance; } }
當getInstance()方法在第一次調用的時候,它第一次讀取SingleHolder.instance,導致SingleHolder類得到初始化。而這個類在裝載並初始化的時候,會初始化它的靜態域,從而創建Singleton實例。由於是靜態的域,因此只會被虛擬機在裝載類的時候初始化一次,有虛擬機來保證它的線程安全性。