在編程中,有些場景,是這樣的: 有些對象我們只需要一個,比如線程池對象、緩存、系統全局配置文件對象等。 這樣我們就保證一個在全局使用的類不被頻繁的創建與銷毀,節省系統資源。
實現單例模式的三大要點:1、首先要確保全局只有一個類的實例。
因此至少類的構造器要私有化。
2、單例的類只能自己創建自己的實例。
因為構造器私有了,但是還要有一個實例,所有就只能自己創建。
3、單例類必須能夠提供自己的唯一實例給其他類。
因此要有一個公共的方法能返回該單例類的唯一實例。
單例的幾種實現1、懶漢模式(單線程使用)--懶漢式就是不在系統加載時就創建類的單例,而是在第一次使用實例的時候再創建。--多線程禁止使用。延遲加載(LazyLoading)很明顯嚴格來講不算單例模式,因為線程不安全,多線程下,可能產生多個對象。不是單例。public class Lanhandanli{
//第一步靜態私有化對象
private static Lanhandanli instance = null;
//第二步私有化空參構造
privateLanhandanli(){}
//第三步提供獲取對象方法
piublic static Lanhandanli getInstance(){
//判斷對象是否為null,如果為空就創建
if(instance==null){
instance = new Lasnhandanli();
}
return instance;
}}
2、餓漢模式--靜態常量方式--在加載類的時候就會創建類的單例,並保存在類中。--靜態常量方式(線程安全--類加載是就初始化實例,避免了多線程同步問題。天然線程安全)public class Ehandanli{
private static finall Ehandanli instance = new Ehandanli();
private Ehandanli(){}
public static Ehandanli getInstance(){
return Ehandanli;
}}
3、餓漢模式--靜態代碼塊方式--靜態代碼塊方式(線程安全)其實就是上面靜態常量餓漢式 實現上稍微變動一下,將類的實例化放到了靜態代碼塊中而已。public class Ehandanli{
private static finall Ehandanli instance;
static {
instance = new Ehandanli();
}
private Ehandanli(){}
public static Ehandanli getInstance(){
return Ehandanli;
}}
4、懶漢模式--synchronized同步鎖--synchronized同步鎖(線程安全--與上面懶漢模式實現唯一不同的是,獲取實例getInstance()方法上加了同步鎖,保證線程多線程下單例。但是效率有所折損,不過還好)public class Lanhandanli{
//第一步靜態私有化對象
private static Lanhandanli instance = null;
//第二步私有化空參構造
privateLanhandanli(){}
//第三步提供獲取對象方法
piublic static synchronized Lanhandanli getInstance(){
//判斷對象是否為null,如果為空就創建
if(instance==null){
instance = new Lasnhandanli();
}
return instance;
}}
5、懶漢模式--雙重校驗鎖--DCL--雙重校驗鎖--DCL,即 double-checked locking使用到(volatile and synchronized)此種實現中不用每次需要獲得鎖,減少了獲取鎖和等待的事件。注意volatile關鍵字的使用,保證了各線程對singleton靜態實例域修改的可見性。public class Lanhandanli{
//第一步靜態私有化對象
private volatile static Lanhandanli instance = null;
//第二步私有化空參構造
privateLanhandanli(){}
//第三步提供獲取對象方法
piublic static synchronized Lanhandanli getInstance(){
//判斷對象是否為null,如果為空就創建
if(instance==null){
instance = new Lasnhandanli();
}
return instance;
}
//第一步靜態私有化對象
private volatile static Lanhandanli instance = null;
//第二步私有化空參構造
privateLanhandanli(){}
//第三步提供獲取對象方法
piublic static Lanhandanli getInstance(){
if(instance==null){
synchronized(LanHandanli.class){
if(instance == null){
instance = new Lasnhandanli();
}
}
}
return instance;
}}描述:這種方式稱為雙重檢查鎖(Double-Check Locking),需要注意的是,如果使用雙重檢查鎖定來實現懶漢式單例類,需要在靜態成員變量instance之前增加修飾符volatile,被volatile修飾的成員變量可以確保多個線程都能夠正確處理,且該代碼只能在JDK 1.5及以上版本中才能正確執行。由於volatile關鍵字會屏蔽Java虛擬機所做的一些代碼優化,可能會導致系統運行效率降低,因此即使使用雙重檢查鎖定來實現單例模式也不是一種完美的實現方式。
6、懶漢模式--靜態內部類實現單例--靜態內部類實現單例(線程安全、效率高)zhe這種方式下Singleton類被加載了,inatance不一定被初始化。因為SingletonHolder累沒有被主動使用,只有通過顯示調用getInstance方法時,才會顯示裝載SingletonHolder類,從而實例化instance.注意內部類SingletonHolder要用static修飾且其中的的靜態變量instance必須睡final修飾的。public calss Singleton{ //內部類 private static class SingletonHolder { private static final Singleton instance = new Singleton(); }
private Singleton(){};
public static final Singleton getInstance(){
return SingletonHolder.instance;
}}描述:餓漢式單例類不能實現延遲加載,不管將來用不用始終佔據內存;懶漢式單例類線程安全控制煩瑣,而且性能受影響。可見,無論是餓漢式單例還是懶漢式單例都存在這樣那樣的問題,有沒有一種方法,能夠將兩種單例的缺點都克服,而將兩者的優點合二為一呢?答案是:Yes!下面我們來學習這種更好的被稱之為Initialization Demand Holder (IoDH)的技術。在IoDH中,我們在單例類中增加一個靜態(static)內部類,在該內部類中創建單例對象,再將該單例對象通過getInstance()方法返回給外部使用。由於靜態單例對象沒有作為Singleton的成員變量直接實例化,因此類加載時不會實例化Singleton,第一次調用getInstance()時將加載內部類SingletonHolder,在該內部類中定義了一個static類型的變量instance,此時會首先初始化這個成員變量,由Java虛擬機來保證其線程安全性,確保該成員變量只能初始化一次。由於getInstance()方法沒有任何線程鎖定,因此其性能不會造成任何影響。通過使用IoDH,我們既可以實現延遲加載,又可以保證線程安全,不影響系統性能,不失為一種最好的Java語言單例模式實現方式**(其缺點是與程式語言本身的特性相關,很多面向對象語言不支持IoDH)