我從我的博客里把我的文章粘貼過來吧,對于單例模式模式應該有比較清楚的解釋:
成都網站建設哪家好,找創新互聯建站!專注于網頁設計、成都網站建設、微信開發、成都小程序開發、集團成都企業網站定制等服務項目。核心團隊均擁有互聯網行業多年經驗,服務眾多知名企業客戶;涵蓋的客戶類型包括:廣告設計等眾多領域,積累了大量豐富的經驗,同時也獲得了客戶的一致贊賞!
單例模式在我們日常的項目中十分常見,當我們在項目中需要一個這樣的一個對象,這個對象在內存中只能有一個實例,這時我們就需要用到單例。
一般說來,單例模式通常有以下幾種:
1.饑漢式單例
public class Singleton {
private Singleton(){};
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}
這是最簡單的單例,這種單例最常見,也很可靠!它有個唯一的缺點就是無法完成延遲加載——即當系統還沒有用到此單例時,單例就會被加載到內存中。
在這里我們可以做個這樣的測試:
將上述代碼修改為:
public class Singleton {
private Singleton(){
System.out.println("createSingleton");
};
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
public static void testSingleton(){
System.out.println("CreateString");
}
}
而我們在另外一個測試類中對它進行測試(本例所有測試都通過Junit進行測試)
public class TestSingleton {
@Test
public void test(){
Singleton.testSingleton();
}
}
輸出結果:
createSingleton
CreateString
我們可以注意到,在這個單例中,即使我們沒有使用單例類,它還是被創建出來了,這當然是我們所不愿意看到的,所以也就有了以下一種單例。
2.懶漢式單例
public class Singleton1 {
private Singleton1(){
System.out.println("createSingleton");
}
private static Singleton1 instance = null;
public static synchronized Singleton1 getInstance(){
return instance==null?new Singleton1():instance;
}
public static void testSingleton(){
System.out.println("CreateString");
}
}
上面的單例獲取實例時,是需要加上同步的,如果不加上同步,在多線程的環境中,當線程1完成新建單例操作,而在完成賦值操作之前,線程2就可能判
斷instance為空,此時,線程2也將啟動新建單例的操作,那么多個就出現了多個實例被新建,也就違反了我們使用單例模式的初衷了。
我們在這里也通過一個測試類,對它進行測試,最后面輸出是
CreateString
可以看出,在未使用到單例類時,單例類并不會加載到內存中,只有我們需要使用到他的時候,才會進行實例化。
這種單例解決了單例的延遲加載,但是由于引入了同步的關鍵字,因此在多線程的環境下,所需的消耗的時間要遠遠大于第一種單例。我們可以通過一段測試代碼來說明這個問題。
public class TestSingleton {
@Test
public void test(){
long beginTime1 = System.currentTimeMillis();
for(int i=0;i100000;i++){
Singleton.getInstance();
}
System.out.println("單例1花費時間:"+(System.currentTimeMillis()-beginTime1));
long beginTime2 = System.currentTimeMillis();
for(int i=0;i100000;i++){
Singleton1.getInstance();
}
System.out.println("單例2花費時間:"+(System.currentTimeMillis()-beginTime2));
}
}
最后輸出的是:
單例1花費時間:0
單例2花費時間:10
可以看到,使用第一種單例耗時0ms,第二種單例耗時10ms,性能上存在明顯的差異。為了使用延遲加載的功能,而導致單例的性能上存在明顯差異,
是不是會得不償失呢?是否可以找到一種更好的解決的辦法呢?既可以解決延遲加載,又不至于性能損耗過多,所以,也就有了第三種單例:
3.內部類托管單例
public class Singleton2 {
private Singleton2(){}
private static class SingletonHolder{
private static Singleton2 instance=new Singleton2();
}
private static Singleton2 getInstance(){
return SingletonHolder.instance;
}
}
在這個單例中,我們通過靜態內部類來托管單例,當這個單例被加載時,不會初始化單例類,只有當getInstance方法被調用的時候,才會去加載
SingletonHolder,從而才會去初始化instance。并且,單例的加載是在內部類的加載的時候完成的,所以天生對線程友好,而且也不需要
synchnoized關鍵字,可以說是兼具了以上的兩個優點。
4.總結
一般來說,上述的單例已經基本可以保證在一個系統中只會存在一個實例了,但是,仍然可能會有其他的情況,導致系統生成多個單例,請看以下情況:
public class Singleton3 implements Serializable{
private Singleton3(){}
private static class SingletonHolder{
private static Singleton3 instance = new Singleton3();
}
public static Singleton3 getInstance(){
return SingletonHolder.instance;
}
}
通過一段代碼來測試:
@Test
public void test() throws Exception{
Singleton3 s1 = null;
Singleton3 s2 = Singleton3.getInstance();
//1.將實例串行話到文件
FileOutputStream fos = new FileOutputStream("singleton.txt");
ObjectOutputStream oos =new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
//2.從文件中讀取出單例
FileInputStream fis = new FileInputStream("singleton.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (Singleton3) ois.readObject();
if(s1==s2){
System.out.println("同一個實例");
}else{
System.out.println("不是同一個實例");
}
}
輸出:
不是同一個實例
可以看到當我們把單例反序列化后,生成了多個不同的單例類,此時,我們必須在原來的代碼中加入readResolve()函數,來阻止它生成新的單例
public class Singleton3 implements Serializable{
private Singleton3(){}
private static class SingletonHolder{
private static Singleton3 instance = new Singleton3();
}
public static Singleton3 getInstance(){
return SingletonHolder.instance;
}
//阻止生成新的實例
public Object readResolve(){
return SingletonHolder.instance;
}
}
再次測試時,就可以發現他們生成的是同一個實例了。
單例模式大致有五種寫法,分別為懶漢,惡漢,靜態內部類,枚舉和雙重校驗鎖。
1、懶漢寫法,常用寫法
class?LazySingleton{
private?static?LazySingleton?singleton;
private?LazySingleton(){
}
public?static?LazySingleton?getInstance(){
if(singleton==null){
singleton=new?LazySingleton();
}
return?singleton;
}???
}
2、惡漢寫法,缺點是沒有達到lazy loading的效果
class?HungrySingleton{
private?static?HungrySingleton?singleton=new?HungrySingleton();
private?HungrySingleton(){}
public?static?HungrySingleton?getInstance(){
return?singleton;
}
}
3、靜態內部類,優點:加載時不會初始化靜態變量INSTANCE,因為沒有主動使用,達到Lazy loading
class?InternalSingleton{
private?static?class?SingletonHolder{
private?final?static??InternalSingleton?INSTANCE=new?InternalSingleton();
}???
private?InternalSingleton(){}
public?static?InternalSingleton?getInstance(){
return?SingletonHolder.INSTANCE;
}
}
4、枚舉,優點:不僅能避免多線程同步問題,而且還能防止反序列化重新創建新的對象
enum?EnumSingleton{
INSTANCE;
public?void?doSomeThing(){
}
}
5、雙重校驗鎖,在當前的內存模型中無效
class?LockSingleton{
private?volatile?static?LockSingleton?singleton;
private?LockSingleton(){}
//詳見:
public?static?LockSingleton?getInstance(){
if(singleton==null){
synchronized(LockSingleton.class){
if(singleton==null){
singleton=new?LockSingleton();
}
}
}
return?singleton;
}
}
參考自:
你好!
一個常見情景,單例類在多線程環境中違反契約。如果你要一個新手寫出單例模式,可能會得到下面的代碼:
private static Singleton _instance;
public static Singleton getInstance() {
if (_instance == null) {
_instance = new Singleton();
}
return _instance;
}
然后,當你指出這段代碼在超過一個線程并行被調用的時候會創建多個實例的問題時,他很可能會把整個getInstance()方法設為同步(synchronized),就像我們展示的第二段示例代碼getInstanceTS()方法一樣。盡管這樣做到了線程安全,并且解決了多實例問題,但并不高效。在任何調用這個方法的時候,你都需要承受同步帶來的性能開銷,然而同步只在第一次調用的時候才被需要,也就是單例類實例創建的時候。這將促使我們使用雙重檢查鎖模式(double checked locking pattern),一種只在臨界區代碼加鎖的方法。程序員稱其為雙重檢查鎖,因為會有兩次檢查 _instance == null,一次不加鎖,另一次在同步塊上加鎖。這就是使用Java雙重檢查鎖的示例:
public static Singleton getInstanceDC() {
if (_instance == null) { // Single Checked
synchronized (Singleton.class) {
if (_instance == null) { // Double checked
_instance = new Singleton();
}
}
}
return _instance;
}
這個方法表面上看起來很完美,你只需要付出一次同步塊的開銷,但它依然有問題。除非你聲明_instance變量時使用了volatile關鍵字。沒有volatile修飾符,可能出現Java中的另一個線程看到個初始化了一半的_instance的情況,但使用了volatile變量后,就能保證先行發生關系(happens-before relationship)。對于volatile變量_instance,所有的寫(write)都將先行發生于讀(read),在Java 5之前不是這樣,所以在這之前使用雙重檢查鎖有問題?,F在,有了先行發生的保障(happens-before guarantee),你可以安全地假設其會工作良好。另外,這不是創建線程安全的單例模式的最好方法,你可以使用枚舉實現單例模式,這種方法在實例創建時提供了內置的線程安全。另一種方法是使用靜態持有者模式(static holder pattern)。
/*
* A journey to write double checked locking of Singleton class in Java.
*/
class Singleton {
private volatile static Singleton _instance;
private Singleton() {
// preventing Singleton object instantiation from outside
}
/*
* 1st version: creates multiple instance if two thread access
* this method simultaneously
*/
public static Singleton getInstance() {
if (_instance == null) {
_instance = new Singleton();
}
return _instance;
}
/*
* 2nd version : this definitely thread-safe and only
* creates one instance of Singleton on concurrent environment
* but unnecessarily expensive due to cost of synchronization
* at every call.
*/
public static synchronized Singleton getInstanceTS() {
if (_instance == null) {
_instance = new Singleton();
}
return _instance;
}
/*
* 3rd version : An implementation of double checked locking of Singleton.
* Intention is to minimize cost of synchronization and improve performance,
* by only locking critical section of code, the code which creates instance of Singleton class.
* By the way this is still broken, if we don't make _instance volatile, as another thread can
* see a half initialized instance of Singleton.
*/
public static Singleton getInstanceDC() {
if (_instance == null) {
synchronized (Singleton.class) {
if (_instance == null) {
_instance = new Singleton();
}
}
}
return _instance;
}
}
這就是本文的所有內容了。這是個用Java創建線程安全單例模式的有爭議的方法,使用枚舉實現單例類更簡單有效。我并不建議你像這樣實現單例模式,因為用Java有許多更好的方式。但是,這個問題有歷史意義,也教授了并發是如何引入一些微妙錯誤的。正如之前所說,這是面試中非常重要的一點。
在去參加任何Java面試之前,要練習手寫雙重檢查鎖實現單例類。這將增強你發現Java程序員們所犯編碼錯誤的洞察力。另外,在現在的測試驅動開發中,單例模式由于難以被模擬其行為而被視為反模式(anti pattern),所以如果你是測試驅動開發的開發者,最好避免使用單例模式。轉載,僅供參考。
這個模式保護類的創建過程來確保只有一個實例被創建,它通過設置類的構造方法為私有來達到這個目的。 要獲得類的實例,單例類可以提供一個方法,如getInstance,來返回類的實例。該方法是唯一可以訪問類來創建實例的方法。 下面是單例的一個例子:Java代碼publicclassSingletonPattern{privatestaticSingletonPatterninstance;privateSingletonPattern(){}publicstatic synchronized SingletonPatterngetInstance(){if(instance==null){instance=newSingletonPattern();}returninstance;}} 當我們要實現單例的時候,有如下的規則需要遵循: 從上面的示例代碼中可以看出,一個單例類有一個靜態的屬性來保存它唯一的實例 需要將類的構造方法設置為private。這樣你不允許其他任何類來創建單例類的實例,因為它們不能訪問單例類的構造方法。
單例模式:就是一個類僅創建一個對象;
public?class?Singleton?{
private?static?volatile?Singleton?singleton?=?null;
private?Singleton(){}//?構造方法
public?static?Singleton?getSingleton(){//?單例模式
if(singleton?==?null){
synchronized?(Singleton.class){
if(singleton?==?null){
singleton?=?new?Singleton();
}
}
}
return?singleton;
}????
}
JAVA
單例模式的幾種實現方法
1.餓漢式單例類
package
pattern.singleton;
//
餓漢式單例類
.
在類初始化時,已經自行實例化
public
class
Singleton1
{
//
私有的默認構造子
private
Singleton1()
{}
//
已經自行實例化
private
static
final
Singleton1
single
=
new
Singleton1();
//
靜態工廠方法
public
static
Singleton1
getInstance()
{
return
single;
}
}
2.
懶漢式單例類
package
pattern.singleton;
//
懶漢式單例類
.
在第一次調用的時候實例化
public
class
Singleton2
{
//
私有的默認構造子
private
Singleton2()
{}
//
注意,這里沒有
final
private
static
Singleton2
single;
//
只實例化一次
static
{
single
=
new
Singleton2();
}
//
靜態工廠方法
public
synchronized
static
Singleton2
getInstance()
{
if
(single
==
null
)
{
single
=
new
Singleton2();
}
return
single;
}
}
在上面給出懶漢式單例類實現里對靜態工廠方法使用了同步化,以處理多線程環境。有些設計師在這里建議使用所謂的
"
雙重檢查成例
".
必須指出的是,
"
雙重檢查成例
"
不可以在
Java
語言中使用。不十分熟悉的讀者,可以看看后面給出的小節。
同
樣,由于構造子是私有的,因此,此類不能被繼承。餓漢式單例類在自己被加載時就將自己實例化。即便加載器是靜態的,在餓漢
式單例類被加載時仍會將自己實例化。單從資源利用效率角度來講,這個比懶漢式單例類稍差些。從速度和反應時間角度來講,
則
比懶漢式單例類稍好些。然而,懶漢式單例類在實例化時,必須處
理好在多個線程同時首次引用此類時的訪問限制問題,特別是當單例類作為資源控制器,在實例化時必然涉及資源初始化,而資源
初始化很有可能耗費時間。這意味著出現多線程同時首次引用此類的機率變得較大。
餓漢式單例類可以在
Java
語言內實現,
但不易在
C++
內實現,因為靜態初始化在
C++
里沒有固定的順序,因而靜態的
m_instance
變量的初始化與類的加載順序沒有保證,可能會出問題。這就是為什么
GoF
在提出單例類的概念時,舉的例子是懶
漢式的。他們的書影響之大,以致
Java
語言中單例類的例子也大多是懶漢式的。實際上,本書認為餓漢式單例類更符合
Java
語
言本身的特點。
3.
登記式單例類
.
package
pattern.singleton;
import
java.util.HashMap;
import
java.util.Map;
//
登記式單例類
.
//
類似
Spring
里面的方法,將類名注冊,下次從里面直接獲取。
public
class
Singleton3
{
private
static
MapString,Singleton3
map
=
new
HashMapString,Singleton3();
static
{
Singleton3
single
=
new
Singleton3();
map.put(single.getClass().getName(),
single);
}
//
保護的默認構造子
protected
Singleton3(){}
//
靜態工廠方法
,
返還此類惟一的實例
public
static
Singleton3
getInstance(String
name)
{
if
(name
==
null
)
{
name
=
Singleton3.
class
.getName();
System.out.println("name
==
null"+"---name="+name);
}
if
(map.get(name)
==
null
)
{
try
{
map.put(name,
(Singleton3)
Class.forName(name).newInstance());
}
catch
(InstantiationException
e)
{
e.printStackTrace();
}
catch
(IllegalAccessException
e)
{
e.printStackTrace();
}
catch
(ClassNotFoundException
e)
{
e.printStackTrace();
}
}
return
map.get(name);
}
//
一個示意性的商業方法
public
String
about()
{
return
"Hello,
I
am
RegSingleton.";
}
public
static
void
main(String[]
args)
{
Singleton3
single3
=
Singleton3.getInstance(
null
);
System.out.println(single3.about());
}
}
本文名稱:java代碼實現單例,java單例模式例子
瀏覽路徑:http://m.kartarina.com/article42/hddiec.html
成都網站建設公司_創新互聯,為您提供App開發、建站公司、網站收錄、App設計、網站設計、移動網站建設
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯