生活随笔
收集整理的這篇文章主要介紹了
单例模式(饿汉式和懒汉式)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
??以前學習單例的時候,只理解了簡單部分。這次看DRP,對單例的餓漢式和懶漢式有了一些認識和對比。
? ?在實際的開發中,有些地方需要一個類只有一個實例。比如:網站在線人數的計數器,再比如IDE中的工具箱之類的等等。當需要這個類只有一個實例時,我們就需要使用到單例模式。單例模式有兩種實現方式:懶漢式(延遲加載)和 餓漢式(預加載)。
? ?目前遇到的情況使用餓漢式的比較多,也因為它比較簡單。代碼:
[java]?view plaincopy
public?class?ClientManager?{????? ?????? ????private?static?ClientManager?instance?=?new?ClientManager();????? ?????? ????public?ClientManager(){?}?? ?????? ????public?static?ClientManager?getInstance(){??????? ????????return?instance;?? ????}?? }??
? ?餓漢式:在類加載時就完成了初始化,所以類加載較慢,但獲取對象的速度快。這里getInstance()是static的,不用同步(類加載時已初始化,不會有多線程的問題)
? ?餓漢式程序運行過程中會節省時間,但是實例不管有沒有用到都會占用空間。在這方面懶漢式似乎比餓漢式優化。我們先看看代碼:
[java]?view plaincopy
public?class?ClientManager?? ?{?? ????private?static?ClientManager?instance?=?null;?? ?????? ????private?ClientManager(){?? ?????????? ????}?? ?? ????public?static??ClientManager?getInstance(){??????? ????????if(instance?==?null){?? ????????????return?new?ClientManager();?? ????????}?? ????????return?instance;?? ????}?? }??
? ? 比較懶,在類加載時,不創建實例,因此類加載速度快,但運行時獲取對象的速度慢。這里getInstance()是static的。我們來想象一下:要使用ClientManager,直接調用類的getInstance()方法。第一次的時候發現instance是null,然后就新建一個對象,返回出去;第二次再使用的時候,因為這個instance是static的,所以已經不是null了,因此不會再創建對象,直接將其返回。那么為什么有同步的問題?
? ? 線程A希望使用ClientManager,調用getInstance()方法。因為是第一次調用,A就發現instance是null的,于是它開始創建實例,就在這個時候,CPU發生時間片切換,線程B開始執行,它要使用ClientManager,調用getInstance()方法,同樣檢測到instance是null——注意,這是在A檢測完之后切換的,也就是說A并沒有來得及創建對象——因此B開始創建。B創建完成后,切換到A繼續執行,因為它已經檢測完了,所以A不會再檢測一遍,它會直接創建對象。這樣,線程A和B各自擁有一個ClientManager的對象——單例失敗!
? ? 對于這種情況,我們很容易就想到加鎖來解決。那么加鎖后是怎么樣的?
[java]?view plaincopy
public?class?ClientManager{??? ?? ??private?static?ClientManager?instance?=?null;??? ?????? ??public?synchronized?static?ClientManager?getInstance()?{??????? ????if(instance?==?null)?{??? ??????instance?=?new?ClientManager();??? ????}??? ????return?instance;??? ??}??? ?????? ??private?ClientManager()?{??? ??????? ??}??? ?????? ?? }??
? ?getInstance()加上同步鎖,一個線程必須等待另外一個線程創建完成后才能使用這個方法,這就保證了單例的唯一性。但是還有出現這樣一個性能問題:每次調用getInstants時都需要加鎖,會降低運行速度。所以我們還可以進一步改進。
[java]?view plaincopy
public?class?ClientManager?{??? ?? ??private??static?ClientManager?instance?=?null;??? ?? ??public?static?ClientManager?getInstance()?{??? ????if?(instance?==?null)?{??? ??????synchronized?(ClientManager.class)?{??? ????????if(instance?==?null)?{??? ??????????instance?=?new?ClientManager();??? ????????}??? ??????}??? ????}??? ????return?instance;??? ??}??? ?? ??private?ClientManager()?{??? ?? ?? ??}???
? ?這樣的方法可以雙重鎖定。我們一般用讓線程每次都加鎖,而只是實例未被創建的時候再加鎖處理,同時也能保證多線程的安全。為什么要進行兩次instance==null的判斷,這個交給大家自己想一下吧。就按照上面線程A和線程B這樣的方式。
? ?最后,總結一下餓漢式和懶漢式的區別:餓漢式類加載時已初始化,不會有多線程的問題,使用簡單。懶漢式是在需要時才對類進行實例化,但是有多線程問題,需要該考慮怎么加鎖的問題。
總結
以上是生活随笔為你收集整理的单例模式(饿汉式和懒汉式)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。