日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

java设计模式 单例_java设计模式一(单例模式singleton)

發布時間:2025/3/21 asp.net 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java设计模式 单例_java设计模式一(单例模式singleton) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1 概述

如果要保證系統里一個類最多只能存在一個實例時,我們就需要單例模式。這種情況在我們應用中經常碰到,例如緩存池、數據庫連接池、線程池、一些應用服務實例等。在多線程環境中為了保證實例的唯一性其實并不簡單,下面介紹如何實現單例模式。

2 最簡單的單例

為了限制該類的對象被隨意地創建,我們保證該類構造方法是私有的,這樣外部類就無法創建該類型的對象了,另外,為了給客戶對象提供對此單例對象的使用,我們為它提供一個全局訪問點,代碼如下所示:

publicclass Singleton {

privatestatic Singleton intance = new Singleton();

private Singleton() {// 將構造方法私有化

}

publicstatic Singleton getIntance() {

return intance;

}

}

代碼注解:

Singleton類的只有一個構造方法,它是被private修飾的,客戶對象無法創建該類實例。我們為此單例實現的全局訪問點是public static Singleton getInstance()方法,注意instance變量是私有的外界無法訪問的。

讀者還可以定義instance變量是public的,這樣把屬性直接暴露給其他對象,就沒必要實現public static Singleton getInstance()方法但是可讀性沒有方法來的直接,而且把該實例變量的名字直接暴露給客戶程序,增加了代碼的耦合度,如果改變此變量名稱,會引起客戶類的改變。還有一點,如果該實例需要比較復雜的初始化過程時,把這個過程應該寫在static{…}代碼塊中。

此實現是線程安全的,當多個線程同時去訪問該類的getInstance()方法時,不會初始化多個不同的對象,這是因為JVM(Java Virtual Machine)在加載此類時,對于static屬性的初始化只能由一個線程執行且僅一次。由于此單例提供了靜態的公有方法,那么客戶使用單例模式的代碼也就非常簡單了如下所示Singleton singleton = Singleton.getIntance()

3進階

3.1 延遲創建

如果出于性能等的考慮,我們希望延遲實例化單例對象,Static屬性在加載類是就會被初始化,只有在第一次使用該類的實例時才去實例化,我們應該怎么辦呢,這個其實并不難做到,我們把單例的實例化過程移至getInstance()方法,而不在加載類時預先創建。當訪問此方法時,首先判斷該實例是不是已經被實例化過了,如果已被初始化,則直接返回這個對象的引用,否則,創建這個實例并初始化最后返回這個對象引用。代碼片段如下所示

publicclass UnThreadSafeSingleton {

privatestatic UnThreadSafeSingleton intance = null;

private UnThreadSafeSingleton() {

System.out.print("非線程安全的單例");

}

publicstatic UnThreadSafeSingleton getIntance() {

if (intance == null) {

intance = new UnThreadSafeSingleton();

}

returnintance;

}

}

我們使用這句if(instatnce ==null)判斷是否實例化完成了。此方法不是線程安全的,接下來我們將會討論。

3.2線程安全

上節我們創建了可延遲初始化的單例,然而不幸的是,在高并發的環境中getInstance()方法返回了多個指向不同的該類實例,究竟是什么原因呢,我們針對此方法,給出兩個線程并發訪問getInstance()方法時的一種情況,如下所示

1

2

3

4

5

6

Thread1

if(instatnce ==null)

instatnce = new UnThreadSafeSingelton()

return instance

Thread2

if(instatnce ==null)

instatnce = new UnThreadSafeSingelton()

return instance

如果這兩個線程按照上述步驟執行,不難發現,在時刻1和2,由于還沒有創建單例對象,Thread1和Thread2都會進入創建單例實例的代碼塊分別創建實例。在時刻3,Thread1創建了一個實例對象,但是Thread2此時已無法知道,繼續創建一個新的實例對象,于是這兩個線程持有的實例并非為同一個。更為糟糕的是,在沒有自動內存回收機制的語言平臺上運行這樣的單例模式,例如使用C++編寫此模式,因為我們認為創建了一個單例實例,忽略了其他線程所產生的對象,不會手動去回收它們,引起了內存泄露。為了解決這個問題我們給此方法添加synchronized關鍵字,代碼如下

public class ThreadSafeSingleton {

private static ThreadSafeSingleton intance = null;

private ThreadSafeSingleton() {

System.out.print("線程安全的單例");

}

public static synchronized ThreadSafeSingleton getIntance() {

if (intance == null) {

intance = new ThreadSafeSingleton();

}

return intance;

}

}

3.3 Double-Check Locking

上述途徑雖然實現了多線程的安全訪問,但是在多線程高并發訪問的情況下,給此方法加上synchronized關鍵字會使得性能大不如前。我們仔細分析一下不難發現,使用了synchronized關鍵字對整個getInstance()方法進行同步是沒有必要的,我們只要保證實例化這個對象的那段邏輯被一個線程執行就可以了,而返回引用的那段代碼是沒有必要同步的。按照這個想法,我們的代碼片段大致如下所示

publicclass DoubleCheckSingleton {

private volatile static DoubleCheckSingleton intance = null;

private DoubleCheckSingleton() {

System.out.print("線程安全的單例---多線程高并發訪問");

}

publicstatic DoubleCheckSingleton getIntance() {

synchronized (DoubleCheckSingleton.class) {

if (intance == null) {

intance = new DoubleCheckSingleton();

}

}

returnintance;

}

}

代碼注解

在getInstance()方法里,我們首先判斷此實例是否已經被創建了,如果還沒有創建,首先使用synchronized同步實例化代碼塊。在同步代碼塊里,我們還需要再次檢查是否已經創建了此類的實例,這是因為,如果沒有第二次檢查,這時有兩個線程Thread A和Thread B同時進入該方法,它們都檢測到instatnce為null,不管哪一個線程先占據同步鎖創建實例對象,都不會阻止另外一個線程繼續進入實例化代碼塊重新創建實例對象,這樣同樣會生成兩個實例對象。所以,我們在同步的代碼塊里,進行第二次判斷判斷該對象是否已被創建。

正是由于使用了兩次的檢查我們稱之為double-checked locking模式。

屬性instatnce是被volatile修飾的因為volatile具有synchronized的可見性特點,也就是說線程能夠自動發現volatile變量的最新值。這樣,如果instatnce實例化成功,其他線程便能立即發現。注意,此程序只有在JAVA 5及以上版本才能正常運行,在以前版本不能保證其正常運行。這是由于Java平臺的內存模式容許out-of-order writes引起的,假定有兩個線程Thread 1和Thread 2它們執行以下步驟:

1. Thread 1發現instatnce沒有被實例化,它獲得鎖并去實例化此對象,JVM容許在沒有完全實例化完成時instance變量就指向此實例,因為這些步驟可以是out-of-order writes的,此時instance==null為false,之前的版本即使用volatile關鍵字修飾也無效。

2.在初始化完成之前Thread 2進入此方法,發現instance已經不為null了,Thread 2便認為該實例初始化完成了,使用這個未完全初始化的實例對象,則很可能引起系統的崩潰。

3.4 Initialization on demand holder

要使用線程安全的延遲的單例初始化,我們還有一種方法,稱為Initialization on demand holder模式,代碼如下所示

public class LazyLoadedSingleton {

private LazyLoadedSingleton() {

System.out.println("要使用線程安全的延遲的單例初始化我們還有一種方法 稱為Initialization on demand holder模式");

}

private static class LazyHolder {

private static final LazyLoadedSingleton instance = new LazyLoadedSingleton();

}

public static LazyLoadedSingleton getIntance() {

return LazyHolder.instance;

}

}

3.5 的序列化Singleton

如果單例類實現Serializable接口,這時我們得特別注意,因為我們知道在默認情況下,每次反序列化Desierialization總會創建一個新的實例對象,這樣一個系統會出現多個對象供使用。我們應該怎么辦呢,熟悉Java序列化的讀者可能知道,我們需要在readResolve()方法里做文章,此方法在反序列化完成之前被執行,我們在此方法里替換掉反序列化出來的那個新的實例,讓其指向內存中的那個單例對象即可,代碼實現如下

public class SerialibleSingleton implements Serializable {

private static final long serialVersionUID = 6421044614200154010L;

static SerialibleSingleton instance = new SerialibleSingleton();

private SerialibleSingleton() {

}

private Object readResolve() {

return instance;

}

}

方法readResolve()直接返回singleton單例,這樣,我們在內存中始終保持了一個唯一的單例對象。

總結

以上是生活随笔為你收集整理的java设计模式 单例_java设计模式一(单例模式singleton)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。