先不論單例模式的寫法,有些方面是相同的,比如都需要將唯一的對象設(shè)置為static的,都需要將構(gòu)造方法private化,代碼如下:
在象州等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都網(wǎng)站設(shè)計、成都網(wǎng)站制作 網(wǎng)站設(shè)計制作按需設(shè)計網(wǎng)站,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),營銷型網(wǎng)站建設(shè),外貿(mào)網(wǎng)站建設(shè),象州網(wǎng)站建設(shè)費用合理。
public class MyInstance { private static MyInstance instance; private MyInstance(){}
}
第一種:最原始的單例模式,代碼如下:
public static MyInstance getInstance(){ if(instance==null){ instance=new MyInstance();
} return instance;
}
多線程并發(fā)時,可能會出現(xiàn)重復(fù)new對象的情況,因此不提倡使用。
第二種:將整個方法塊進行加鎖,保證線程安全。
public static synchronized MyInstance getInstance(){ if(instance==null){ instance=new MyInstance();
} return instance;
}
這種代碼下,每條線程都會依次進入方法塊內(nèi)部,雖然實現(xiàn)了單例,但是影響了運行效率,可以使用但是也不怎么提倡。
第三種:進一步優(yōu)化的方法。
public static MyInstance getsInstance(){ synchronized (MyInstance.class){ if(instance==null){ instance=new MyInstance(); return instance;
}else{ return instance;
}
}
}
這種方式只是第二種方法的一種優(yōu)化,但是優(yōu)化有限。
(以下的幾種方法比較推薦使用)
第四種:雙層判斷加鎖,效率影響小且保證了線程安全。
public static MyInstance getsInstance() { if (instance == null) { synchronized (MyInstance.class) { if(instance==null){ instance=new MyInstance();
}
}
} return instance;
}
這種方法是對第二種和第三種方法的進一步優(yōu)化,比較推薦使用。
第五種:內(nèi)部類實現(xiàn)單例,不用線程鎖來實現(xiàn)效率的提升。
public class MyInstance { private MyInstance() {
} public static MyInstance getInstance(){ return MyInstanceHolder.instance;
} private static class MyInstanceHolder{ private static MyInstance instance=new MyInstance();
}
}
在內(nèi)部類中new對象,再將內(nèi)部類的對象返回,這種方法是使用了java中class加載時互斥的原理來實現(xiàn)了線程的安全。不加線程鎖也使得運行效率不會受到較大的影響。比較提倡。
本文主要記錄使用單例模式的幾種形式,并分析各自的優(yōu)缺點。使用單例模式可以避免重復(fù)創(chuàng)建對象,以此來節(jié)省開銷,首先了解一下單例模式的四大原則:
常用的單例模式有:餓漢模式、懶漢模式、雙重鎖懶漢模式、靜態(tài)內(nèi)部類模式、枚舉模式,我們來逐個解釋這些模式的區(qū)別。
關(guān)于 volatile 修飾符,又是一個內(nèi)容,需要理解:
參考(有例子,比較好理解): ,
靜態(tài)內(nèi)部類單例模式的優(yōu)點:
那么有人會問了,如果有多個線程同時訪問 getInstance() 方法,會多次初始化類,然后創(chuàng)建多個對象嗎?答案是不會的,這我們需要了解一下類的加載機制:
虛擬機會保證一個類的clinit()方法在多線程環(huán)境中被正確地加鎖、同步,如果多個線程同時去初始化一個類,那么只會有一個線程去執(zhí)行這個類的clinit()方法,其他線程都需要阻塞等待,直到活動線程執(zhí)行clinit()方法完畢。
所以如果一個類的clinit()方法中有耗時很長的操作,就可能造成多個進程阻塞(需要注意的是,其他線程雖然會被阻塞,但線程喚醒之后不會再次進入clinit()方法。因為在同一個加載器下,一個類只會初始化一次。)
所以靜態(tài)內(nèi)部類單例模式不僅能保證線程的安全性、實例的唯一性、還延遲了單例的實例化。
但是靜態(tài)內(nèi)部類單例模式也有一個 缺點 ,就是無法傳遞參數(shù)。因為它是通過靜態(tài)內(nèi)部類的形式去創(chuàng)建單例的,所以外部就無法傳遞參數(shù)進去。
枚舉單例模式占用的內(nèi)存是靜態(tài)變量的兩倍,所以一般都不使用enum來實現(xiàn)單例。
單例有餓漢模式、懶漢模式、雙重鎖懶漢模式、靜態(tài)內(nèi)部類模式、枚舉模式這幾種形式。
餓漢模式在初始化類時就創(chuàng)建了對象,容易造成資源浪費;懶漢模式在多線程環(huán)境下有風(fēng)險;枚舉模式占用內(nèi)存過高。這三種模式都有明顯的弊端,所以一般不去采用。
雙重鎖懶漢模式使用了 volatile 修飾符,在性能上會差一點點;靜態(tài)內(nèi)部類模式無法傳遞參數(shù)。但是這兩種方式都能保證實例的唯一性,線程的安全性,也不會造成資源的浪費。所以我們在使用單例模式時,可以在這兩種方式中酌情選擇。
參考文章:
單例模式(Singleton)
一、 什么是單例模式
單例模式,簡單點來說就是設(shè)計一個類,使其在任何時候,最多只有一個實例,并提供一個訪問這個實例的全局訪問點。
二、 為什么要單例
在程序中的很多地方,只有一個實例是非常重要的。例如,在windows中,任務(wù)管理器只有一個,無論你點擊多少次打開任務(wù)管理器,任務(wù)管理器也只會生成一個窗口。再例如,在一些軟件中,工具箱是唯一的,無論你點擊多少次打開工具箱,工具箱也只一個。
為什么要這樣設(shè)計呢?因為像任務(wù)管理器或工具箱這樣的程序,只要有一個就足夠完成所有的工作了,多個程序只會白白消耗系統(tǒng)資源,而像任務(wù)管理器這類的程序還會引入多個任務(wù)管理器之間的同步問題,所以對些這些程序來說,只有一個實例或程序是必要的。
三、 為什么需要單例模式
上面講到對于某些程序來說,保持其只有一個實例是必要的,但是如何保證一個程序或一個類只有一個實例呢?下面從類的角度來解說。