問題是在哪里:用透視修飾全文(使用透視修飾全文)
767
2025-03-31
文章目錄
故事線
單例模式
單例類代碼實現
提升部分
多線程下的單例模式
餓漢式
懶漢還是餓漢?
單例模式的優(yōu)缺點
優(yōu)點
缺點
故事線
我有一個好朋友廣軍,為了體驗生活,在學校旁邊盤了個店面,開了家奶茶漢堡店。
店面新開張,首先要做的事兒就是去工商局登記一下,然后做個章。
到了工商局,人家給他來了個友情提示:XXX只為私密文件,請不要給別人亂玩,出了啥事兒你需要負責。
登記完,做了章,做個店就算是開起來了,他開始當上了店長,剛開始事情多啊,他又要招聘,又要采購,又要宣傳,又要會計算賬···
但是這些事兒也只能他來干,親力親為,先把這個店扶起來。
單例模式
那這個故事就很好的契合了單例模式的應用場景,所以我這個朋友想和你聊聊單例模式。
什么是單例模式呢?
在項目中,有些類是需要對它們進行“計劃生育”的,即這個類只能有一個實例,如果出現多個實例則會有數據不一致的風險。
你說我開個店,要是有兩個老板,那今天又個單,我說簽,他又跟人家說不簽;這個員工不積極,我說開,他又說不開···那豈不是亂套?
單例模式:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
巧了,這個模式只有一個類,叫單例類,所以類圖我就不畫了吧。
單例模式的應用場景舉例:牽扯到數據問題,數據庫首當其沖,緩存自然也跑不了。
單例類代碼實現
//這里是.h文件 //老板類單例 class Single_Boss { public: static Single_Boss *instence();//獲取數據庫單例 //重點在這個函數 void run(); private: Single_Boss(); ~Single_Boss(); char *errmsg; static Single_Boss *Boss;//實例 };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//源文件 Single_Boss *Single_Boss::Boss= NULL; Single_Boss::Single_Boss() { cout << "Big Boss" << endl; //debug } Single_Boss::~Single_Boss() { cout<<"Seeyou Boss"<
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
提升部分
多線程下的單例模式
曾經有一份真摯的數據庫擺在我眼前,可惜我沒有去珍惜它,直到我的項目屢屢崩潰,我才知道,如果能重來,我要加個鎖。。。
俱往矣,數風流人物,還看今朝。
來我們重新審視一下下面這段代碼:
Single_Boss* Single_Boss::instence() //1 { if(!Boss) //2 { Boss= new Single_Boss(); //3 } return Boss; }
1
2
3
4
5
6
7
8
如果在多線程情況下,一旦有兩個線程同時進入了 2 ,怎么辦?這不是十分正常的事情嗎?一點防范都沒有,這不是送人頭的行為嗎?
白給!!
所以,改一下:
Single_Boss* Single_Boss::instence() //1 { lock(db_mutex); //假設這個鎖我已經初始化過了 if(!Boss) //2 { Boss= new Single_Boss(); //3 } unlock(db_mutex); //上鎖和解鎖一定要同時寫,就算忘記寫中間步驟,也要先寫解鎖 return Boss; }
1
2
3
4
5
6
7
8
9
10
11
這樣寫,可還行?有沒有 慧眼識豬 的朋友在下面評論區(qū)call個“1”?
這樣寫的話,每次使用數據庫之前都要進行加鎖操作,雖然安全了,但是大大地提高了負荷。
所以,再改一下:
Single_Boss* Single_Boss::instence() //1 { if(!Boss){ //一重鎖定 lock(db_mutex); if(!Boss) //二重鎖定 { Boss= new Single_Boss(); } unlock(db_mutex); //上鎖和解鎖一定要同時寫,就算忘記寫中間步驟,也要先寫解鎖 } return Boss; }
1
2
3
4
5
6
7
8
9
10
11
12
看到這里,可能有的朋友會疑惑:直接把上面那個的 if 判斷和鎖的位置換一下不就完事兒了嗎,為什么要在外面再加上一層,這不是多此一舉嗎?
還是那個問題:
如果你有兩個線程,突破了第一層 if 的防線,及時一個線程會被卡在鎖的外面,但是鎖僅僅只是鎖住了創(chuàng)建單例的部分,當拿到鎖的那個線程釋放了鎖,另一個線程不是照樣能拿到鎖,創(chuàng)建它的“單例”,那這個鎖還有什么意義呢?
而在鎖內鎖外都加一層 if 判斷,當第一個線程進入鎖空間,創(chuàng)建完單例,后面的線程即使是拿到了鎖,也不會去執(zhí)行創(chuàng)建單例的步驟。
這,才是一個好的單例模式,這是單例模式中的“懶漢模式”。
餓漢式
有懶漢式,那也有個餓漢式單例。
什么是餓漢式呢?餓漢模式的關鍵:初始化即實例化
微調上面的代碼:
Single_Boss *Single_Boss::Boss= new Single_Boss(); Single_Boss* Single_Boss::instence() { //不需要進行實例化 //if(!Boss) //{ // Boss= new Single_Boss(); //} return Boss; }
1
2
3
4
5
6
7
8
9
10
11
一般餓漢式加載所導致的弊端是可能我并不想使用實例但是實例已經被構造,相對于懶漢式的用則構造會造成內存的浪費,但是其實現方式很簡單,不用人為加鎖保證線程安全。
懶漢還是餓漢?
選哪個可以看個人喜好吧,這里給出一點建議:
懶漢:在訪問量較小時,采用懶漢實現。這是以時間換空間。 餓漢:由于要進行線程同步,所以在訪問量比較大,或者可能訪問的線程比較多時,采用餓漢實現,可以實現更好的性能。這是以空間換時間。
1
2
3
單例模式的優(yōu)缺點
由于單例模式在內存中只存在一個對象,減少了內存的開支,特別是當對象需要頻繁的創(chuàng)建、銷毀時,而且創(chuàng)建或銷毀時性能又無法優(yōu)化,單例模式的優(yōu)勢就非常明顯。
單例模式可以避免對內存的多重占用。
單例模式可以在系統(tǒng)設置全局的訪問點,優(yōu)化和共享資源訪問。這招我經常用,也很喜歡,因為確實方便,做一個標志位單例類,負責所有數據表的映射處理。(要了解可以私信我)
單例模式一般沒有接口,難以拓展。如果要拓展,考慮重構。
單例模式對于測試是不利的。在并發(fā)環(huán)境中,如果單例沒有完成,是不能進行測試的。
還行吧。
創(chuàng)作不易,順手好習慣,劃著劃著,就找不到了。
數據庫 通用安全
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現本站中有涉嫌抄襲或描述失實的內容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現本站中有涉嫌抄襲或描述失實的內容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。