設計模式 單例模式 懶載入

2021-08-10 15:42:34 字數 3022 閱讀 7957

這篇文章總結幾種比較常用的設計模式,,,不懂得設計模式。。。怎麼敢稱熟悉oop思想。

單例模式的核心結構中只包含乙個被稱為單例類的特殊類,通過單例模式可以保證系統中乙個類只有乙個例項

由於快載入單例模式是執行緒安全的,所以本文只討論懶載入單例模式的執行緒安全問題

版本一 使用懶載入(快載入),程式呼叫時再分配記憶體,然後初始化

class

singleton

return sobj;

}private:

singlenton(){}; //私有化建構函式

static singlenton *sobj;

}signlenton* signlenton::sobj = null;//慢載入,程式呼叫時才會生成,然後初始化

版本二 使用懶載入,考慮到多執行緒安全問題,使用互斥鎖可以解決

1,考慮多執行緒呼叫時,是否存在競態條件

2,考慮競態條件,是否隨著執行緒排程順序的不同,而出現了不同的執行結果

3,發生競態條件的**段稱為臨界區**段,臨界區**段都滿足原子操作,為了保證原子操作,需要給臨界區的**段加上互斥鎖進行控制,if語句不是原子操作,需要加互斥鎖

4,競態條件:計算的正確性取決於多個執行緒的交替執行時許時,就會發生競態條件。例如:1,由於多個執行緒的執行時序是不確定的,從而導致執行結果出現各種問題。2,延遲初始化, (單例模式)懶載入模式,只有在使用時才初始化

class

singleton

pthread_mutex_unlock(&mutex);//解鎖

return sobj;

}~singlenton()

private:

singlenton(){}; //私有化建構函式

static singlenton *sobj;

static ptheread_mutex_t mutex;

}signlenton* signlenton::sobj = null;//懶載入,程式呼叫時才會生成,然後初始化

pthread_mutex_t singleton::mutex = pthread_mutex_initializer;//鎖的初始化

上述互斥鎖滿足了 if語句的原子操作,實現了執行緒安全,但由於程式每呼叫一次,就會產生加鎖解鎖操作,對於單執行緒而言,降低了效率,所以**還需要解決單執行緒的效率問題

版本三 由於存在單執行緒和多執行緒共存問題,提高單執行緒效率,還要滿足多執行緒的執行緒安全問題,需要使用雙if語句

1,單執行緒只需要執行一次互斥鎖的加鎖解鎖,之後的第乙個if語句都是返回false

2,多執行緒仍需要注意競態條件和執行緒安全問題

class

singleton

pthread_mutex_unlock(&mutex);//解鎖

}return sobj;

}~singlenton()

private:

singlenton(){}; //私有化建構函式

static singlenton *sobj;

static ptheread_mutex_t mutex;

}signlenton* signlenton::sobj = null;//懶載入,程式呼叫時才會生成,然後初始化

pthread_mutex_t singleton::mutex = pthread_mutex_initializer;//鎖的初始化

上述**提高了單執行緒效率,並實現執行緒是安全的,呼叫多執行緒時,當鎖被釋放後,執行緒快取sobj的值重新整理到原始記憶體中了,所以下次取值是在記憶體中取,是執行緒安全的。解決了執行緒安全問題,要使單例模式更加完美,還得考慮編譯器和cpu所出現的優化問題。

版本四 考慮編譯器的指令優化和cpu的動態指令優化

1,volatile關鍵字阻止編譯器為了提高速度將乙個變數存在暫存器內而不寫回記憶體

2,volatile關鍵字阻止調整操作sobj變數的指令操作

3,volatile關鍵字可以告訴編譯器此處的變數很有可能被其他地方改變,不能進行優化

4,barrier指令會阻止cpu對指令進行動態換序優化

class singleton

pthread_mutex_unlock(&mutex);

} return sobj;

} private:

singleton(){};

volatile static singleton sobj;

}; volatile singleton* singleton::sobj = null; //懶載入,快載入

編譯器很可能會為了效率而交換兩條不相干的指令,使用volatile可以阻止。

我們為了單執行緒的效率而做個兩個if判斷,但是編譯器很有可能會認為第一次判斷成功以後就沒有必要再判斷第二次,因為編譯器會認為sobj的值在這兩次判斷之間並沒有改變,(編譯器不會考慮另乙個執行緒有沒有改變project的值,他只看到這一段**)但是這兩次判斷是有其作用的,我們並不希望編譯器做這樣的優化。

cpu動態指令換序問題,temp = new int; 實質上由三個步驟:分配記憶體,在記憶體的位置上呼叫建構函式,將記憶體的位址賦給指標sobj。由於cpu亂序執行,很有可能將步驟2和3顛倒,此時會出現一種情況就是;還未呼叫建構函式就把記憶體位址賦給指標,假如有另乙個執行緒呼叫getinstance函式

那麼由於sobj指標非空,該執行緒就會獲取到這個非空但並沒有構造完全的物件指標,如果使用該指標的話,會帶來很惡劣的影響。barrier指令會阻止cpu對指令進行動態換序優化

不囉嗦,最後乙個環節關於兩個關鍵字的最後一點:

/* 1,volatile 只是用於阻止編譯器的一部分優化機制,與鎖安全無關,執行緒安全不能依靠volatile來實現。最多只能作為執行緒安全的額外考慮。

2,barrier 要保證執行緒安全,即使我們使用了鎖等同步操作,我們仍需考慮cpu的亂序執行,要保證執行緒安全,在適當的地方加上barrier,阻止cpu換序操作是必要的。

*/碼完,收工

單例模式懶載入併發

單例雖然沒有快取寫的那麼平凡,如果在getinstance方法上加sychonize會大大影響效能,單例的寫只有在第一使用時才會寫。使用讀寫鎖操作,基本上都上的讀鎖,對其他執行緒訪問沒有影響public class singleton public static singleton getinsta...

單例模式(懶載入 執行緒安全)

1 雙重檢查 單例模式 懶載入 執行緒安全 一 雙重檢查方式 public class singletondoublecheck 雙重檢查 public static synchronized singletondoublecheck getinstance return instance 2 靜態...

ts單例模式 預載入和懶載入

單例模式是針對類的一種設計,讓類只能有乙個例項物件,對於一些沒必要產生第二個例項的類來說建議設計成單例類。單例類的例項化過程是由自身實現的,減少了其他人使用該類時的心智負擔。缺點是單例類由於靜態屬性,無法擴充套件。由於單例類的例項化過程是由自身實現的,所以設計的時候在new這裡就會產生兩種方法。1....