廣州Java培訓之單例設(shè)計模式的線程同步
2018-04-19 13:35:04
1225瀏覽
單例模式是最常用的設(shè)計模式之一,目的是保證一個類只有一個實例。
在項目中的作用:
1、解決因為頻繁創(chuàng)建對象,導致資源消耗過大的問題,如:數(shù)據(jù)庫的連接池,連接池用于創(chuàng)建數(shù)據(jù)庫連接,并對連接進行回收使用,能減少數(shù)據(jù)庫連接的創(chuàng)建次數(shù),從而提高效率,但是連接池對象本身在項目中只需要一個,就需要使用單例模式。類似的還有線程池等。
2、項目中能共享的工具類,如Java中的Runtime類能提供各種運行環(huán)境系統(tǒng)參數(shù),它就被設(shè)計成了單例模式。
實現(xiàn)單例的過程:
1、要保證類只能創(chuàng)建一個對象,就必須隱藏類的構(gòu)造方法,所以要將構(gòu)造方法定義為私有的
2、在類中調(diào)用構(gòu)造方法創(chuàng)建對象,定義靜態(tài)方法用于返回對象。
下面這種單例模式屬于餓漢式單例模式,既一開始就將對象實例化。這樣的做法會導致性能的降低,一般我們會采用延遲加載的方式,既需要對象時再實例化。
classHunger{
//靜態(tài)的實例
privatestaticHungerhunger=newHunger();
//隱藏構(gòu)造方法
privateHunger(){}
//返回靜態(tài)實例
publicstaticHungergetInstance(){
returnhunger;
}
}
下面這種是懶漢式單例模式,既開始不實例化對象,到需要該實例時再實例化,提高了運行效率。
publicclassLazySingleton{
//靜態(tài)的實例
privatestaticLazySingletonsingle=null;
//隱藏構(gòu)造方法
privateLazySingleton(){
System.out.println("創(chuàng)建LazySingleton對象");
}
//返回靜態(tài)實例
publicstaticLazySingletongetInstance(){
if(single==null){
single=newLazySingleton();
}
returnsingle;
}
}
線程安全問題:懶漢單例模式在單線程環(huán)境沒有問題,但在多線程環(huán)境下就會出現(xiàn)問題。
執(zhí)行代碼,"創(chuàng)建LazySingleton對象"這句話會輸出多次,也就是創(chuàng)建了多個對象。
for(inti=0;i<100;i++){
newThread(newRunnable(){
@Override
publicvoidrun(){
System.out.println(LazySingleton.getInstance());
}}).start();
}
分析原因:
假設(shè)線程1滿足getInstance方法中single==null條件后,準備執(zhí)行創(chuàng)建對象的代碼,然后CPU被其它線程搶占,其它線程在getInstance方法中創(chuàng)建對象,然后線程1搶回CPU繼續(xù)執(zhí)行剛才未完成的創(chuàng)建對象代碼,這樣就創(chuàng)建了多個對象。
線程同步問題的解決方法:
1、使用同步方法
publicstaticsynchronizedLazySingletongetInstance(){
if(single==null){
single=newLazySingleton();
}
returnsingle;
}
執(zhí)行剛才多線程的代碼后,我們發(fā)現(xiàn)可以解決同步問題,但是同步方法存在的問題是每個線程進入后都會加鎖,執(zhí)行效率低。
2、使用同步代碼塊配合if使用
publicstaticLazySingletongetInstance(){
if(single==null){
synchronized(LazySingleton.class){
single=newLazySingleton();
}
}
returnsingle;
}
同步塊和if語句配合使用,解決了每次都執(zhí)行上鎖導致的性能問題,但是運行代碼后我們會發(fā)現(xiàn),多線程同步的問題還是可能出現(xiàn),原因是多個線程還是可能會同時進入if語句。
3、使用雙重判斷
在同步塊中再添加一次if判斷就解決了上面的問題,因為就算多個線程同時進入外層if語句,執(zhí)行同步塊還是要進行一次判斷,這樣第一個線程創(chuàng)建對象后,后面的線程就不能再創(chuàng)建了。
publicstaticLazySingletongetInstance(){
//外層的if主要作用是判斷是否需要執(zhí)行同步塊,提高性能
if(single==null){
//靜態(tài)方法中將類作為鎖
synchronized(LazySingleton.class){
//判斷對象是否為空,為空就創(chuàng)建對象
if(single==null){
single=newLazySingleton();
}
}
}
returnsingle;
}
4、靜態(tài)內(nèi)部類
這種方法結(jié)合了餓漢式和懶漢式的特點,如果不調(diào)用getInstance方法,靜態(tài)內(nèi)部類中的創(chuàng)建對象代碼不會執(zhí)行,調(diào)用getInstance后才會執(zhí)行,也就有了懶漢式延遲加載的效果,并且由于對象是直接創(chuàng)建的,還不存在線程同步問題。
classMySingleton{
privateMySingleton(){
}
privatestaticclassSingletonHelp{
staticMySingletoninstance=newMySingleton();
}
publicstaticMySingletongetInstance(){
returnSingletonHelp.instance;
}
}
總結(jié):單例模式的實現(xiàn)一般有餓漢式、懶漢式和靜態(tài)內(nèi)部類等方式,餓漢式創(chuàng)建對象的性能比較低但不存在線程同步問題,懶漢式由于是延遲加載性能更高,但存在線程同步問題,需要使用雙重判斷解決,靜態(tài)內(nèi)部類的方式也能實現(xiàn)單例模式但是代碼可讀性稍差。
如果項目對性能不敏感推薦使用餓漢式,如果是單線程環(huán)境可以使用一般的懶漢式,如果需要多線程則可以使用多重判斷的懶漢式或靜態(tài)內(nèi)部類實現(xiàn)。
以上就是關(guān)于Java開發(fā)單例設(shè)計模式線程同步的詳細介紹,
最后想要了解更多可以登錄扣丁學堂官網(wǎng)咨詢??鄱W堂是專業(yè)的
Java培訓機構(gòu),不僅有專業(yè)的老師和與時俱進的課程體系,還有大量的
Java視頻教程供學員觀看學習,想要學好JavaEE的小伙伴抓緊時間行動吧。Java技術(shù)交流群:670348138。
【關(guān)注微信公眾號獲取更多學習資料】
查看更多關(guān)于“Java開發(fā)資訊”的相關(guān)文章>>
標簽:
Java視頻教程
Java培訓
Java開發(fā)工程師
廣州Java培訓
Java在線教程
Java在線視頻