設計模式一:單例模式
單例模式是最簡單的一個,也是比較常用的一個。所以,首先就拿它先開刷了,哈哈。
說起單例模式,有的人覺得沒啥呀,不就是一個類只產生一個物件麼?
是的,沒錯,但是不知道你有沒有避過這些坑呢???
讓我們一塊來看一下吧。。。
一:什麼是單例?有什麼用?應用於什麼時候?
二:經典單例模式的實現原理+類圖
三:例項專案程式碼
四:對經典單例模式坑的優化方案
一:什麼是單例?有什麼用?應用於什麼時候?
單利模式:確保一個類最多隻有一個物件例項,供大家使用。
有些物件,我們只需要一個物件例項:執行緒池,快取,硬體裝置等;
(比如使用印表機:如果物理上是有一個,而new多個印表機物件,被多個人只用,這樣會造成資料混亂,衝突,結果不一致等問題)
--------------那麼問題來了----------
是否可以使用靜態變數方式實現? -------------目的可以達到
或者程式設計師之間協商出個全域性變數? -------------可以達到
二:經典單例模式的實現原理+類圖
先上類圖:
實現原理:
public class Singleton { //宣告一個靜態物件 private static Singleton uniqeInstance = null; //私有化構造方法 private Singleton() { } //建立獲取物件方法getInstance(); public static Singleton getInstance(){ //判斷物件是否建立 if(uniqeInstance ==null){ //建立物件 uniqeInstance = new Singleton(); } //返回物件 return uniqeInstance; } }
三:例項專案程式碼
初級版:
public class ChocolateFactory1 { // 宣告封裝的私有屬性 private boolean empty; private boolean boiled; // 宣告靜態物件 public volatile static ChocolateFactory1 uniqueInstance = null; // 私有化構造方法 private ChocolateFactory1() { empty = true; boiled = false; } // 建立獲取物件的方法 public static ChocolateFactory1 getInstance() { // 判斷 if (uniqueInstance == null) { // 生成物件 uniqueInstance = new ChocolateFactory1(); } // 返回物件 return uniqueInstance; } // 製作流程中的新增原料的方法: public void fill() { if (empty) { // 新增原料巧克力動作 empty = false; boiled = false; } } // 製作流程中的熬煮的方法: public void boil() { if ((!empty) && (!boiled)) { // 煮沸 boiled = true; } } // 製作流程中的切割排出巧克力的方法: public void drain() { if ((!empty) && boiled) { // 排出巧克力動作 empty = true; } } }
相信大夥看過之後,沒啥問題啊。Are you Fuck keeding me? 別上火,真沒 逗你們。
看下圖哈-------
大佬們,明白了麼?
別捉急,聽我細說------
上面那一段,當我們面對多執行緒訪問的時候,問題就來了,
比如一個極端情況(A執行緒剛剛判斷完確定沒有物件例項建立,正去建立,就是這會兒。卡-----自己的時間片用完了,切換到B執行緒進來了,一看也是要建立物件,而且建立完物件走了,招呼都沒打。等到切換A執行緒,它還是繼續自己走之前的那一步(去建立個物件)
這個流程下來,,他們倆就建立了倆物件,不安全吧,是個坑吧,咋弄啊。
四:對經典單例模式坑的優化方案
別捉急,咱有的是辦法,繼續看--------(方法有三)
1:加個同步鎖;
// 建立獲取物件的方法
// 同步鎖--只准許一個進,出來之後,第二才能進
public static synchronized ChocolateFactory getInstance() {
if (uniqueInstance == null) {
//建立物件
uniqueInstance = new ChocolateFactory();
}
// 返回物件
return uniqueInstance;
}
有了同步鎖,就可以避免了多個執行緒同時訪問的情況。但是同步鎖是消耗資源的,尤其是當很多很多執行緒很多次很多次訪問。
(沒鬧,可能真的就有這種)那就再看看下面的方法,哈哈。
2:加急物件法;
public class ChocolateFactory {
// 宣告封裝的私有屬性
private boolean empty;
private boolean boiled;
// 宣告靜態物件
public volatile static ChocolateFactory uniqueInstance = new ChocolateFactory();
// 私有化構造方法
private ChocolateFactory() {
empty = true;
boiled = false;
}
// 建立獲取物件的方法
public static ChocolateFactory getInstance() {
if (uniqueInstance == null) {
//建立物件
uniqueInstance = new ChocolateFactory();
}
// 返回物件
return uniqueInstance;
}
當然,還是那句話,如果我不想使用這個類的物件,他還是給我建立好了,無奈啊。記憶體資源又讓它給敗家了,別急,咱還有招兒
3:雙重檢查鎖;
public class ChocolateFactory {
// 宣告封裝的私有屬性
private boolean empty;
private boolean boiled;
// 宣告靜態物件 volatile --針對編譯器使用的,處理多執行緒安全問題的關鍵字
public volatile static ChocolateFactory uniqueInstance = null;
// 私有化構造方法
private ChocolateFactory() {
empty = true;
boiled = false;
}
// 建立獲取物件的方法
public static ChocolateFactory getInstance() {
// 判斷
if (uniqueInstance == null) {
// 同步鎖--只准許一個進,出來之後,第二才能進
synchronized (ChocolateFactory.class) {
//再次判斷
if (uniqueInstance == null) {
//建立物件
uniqueInstance = new ChocolateFactory();
}
}
}
// 返回物件
return uniqueInstance;
}
public void fill() {
if (empty) {
// 新增原料巧克力動作
empty = false;
boiled = false;
}
}
public void drain() {
if ((!empty) && boiled) {
// 排出巧克力動作
empty = true;
}
}
public void boil() {
if ((!empty) && (!boiled)) {
// 煮沸
boiled = true;
}
}
}
博主有話說:
作為菜鳥的我也是看了很多資料,在這裡總結整理一個,囉裡囉嗦的 ,彆著急,我就是個話癆。哈哈,希望能給後面的兄弟帶來幫助,也希望能有大佬帶我飛,我要和太陽肩並肩。