欧美成人午夜免费全部完,亚洲午夜福利精品久久,а√最新版在线天堂,另类亚洲综合区图片小说区,亚洲欧美日韩精品色xxx

扣丁學(xué)堂Java培訓(xùn)簡(jiǎn)述Java多線程并發(fā)下的單例模式應(yīng)用

2019-09-17 11:16:02 4776瀏覽

單例模式應(yīng)該是設(shè)計(jì)模式中比較簡(jiǎn)單也非常常見的一個(gè),但是在多線程并發(fā)的環(huán)境下使用卻是不那么簡(jiǎn)單了,本篇文章扣丁學(xué)堂Java培訓(xùn)小編給大家分享一個(gè)在開發(fā)過程中會(huì)遇到的單例模式的應(yīng)用,感興趣的小伙伴就隨小編來了解一下吧。


扣丁學(xué)堂Java培訓(xùn)簡(jiǎn)述Java多線程并發(fā)下的單例模式應(yīng)用


首先我們來看一下單例模式的定義:一個(gè)類有且僅有一個(gè)實(shí)例,并且自行實(shí)例化向整個(gè)系統(tǒng)提供。


例模式的要素:


1、私有的靜態(tài)的實(shí)例對(duì)象

2、私有的構(gòu)造函數(shù)(保證在該類外部,無法通過new的方式來創(chuàng)建對(duì)象實(shí)例)

3、公有的、靜態(tài)的、訪問該實(shí)例對(duì)象的方法


單例模式分為懶漢形和餓漢式


懶漢式:應(yīng)用剛啟動(dòng)的時(shí)候,并不創(chuàng)建實(shí)例,當(dāng)外部調(diào)用該類的實(shí)例或者該類實(shí)例方法的時(shí)候,才創(chuàng)建該類的實(shí)例。(時(shí)間換空間)


優(yōu)點(diǎn):實(shí)例在被使用的時(shí)候才被創(chuàng)建,可以節(jié)省系統(tǒng)資源,體現(xiàn)了延遲加載的思想。


缺點(diǎn):由于系統(tǒng)剛啟動(dòng)時(shí)且未被外部調(diào)用時(shí),實(shí)例沒有創(chuàng)建;如果一時(shí)間有多個(gè)線程同時(shí)調(diào)用LazySingleton.getLazyInstance()方法很有可能會(huì)產(chǎn)生多個(gè)實(shí)例。


例子:


publicclassSingletonClass{
 //私有構(gòu)造函數(shù),保證類不能通過new創(chuàng)建
privateSingletonClass(){}
privatestaticSingletonClassinstance=null;
 publicstaticSingletonClassgetInstance(){
if(instance==null){
 //創(chuàng)建本類對(duì)象
 instance=newSingletonClass();
}
returninstance;
} 
}



餓漢式:應(yīng)用剛啟動(dòng)的時(shí)候,不管外部有沒有調(diào)用該類的實(shí)例方法,該類的實(shí)例就已經(jīng)創(chuàng)建好了。(空間換時(shí)間。)


優(yōu)點(diǎn):寫法簡(jiǎn)單,在多線程下也能保證單例實(shí)例的唯一性,不用同步,運(yùn)行效率高。


缺點(diǎn):在外部沒有使用到該類的時(shí)候,該類的實(shí)例就創(chuàng)建了,若該類實(shí)例的創(chuàng)建比較消耗系統(tǒng)資源,并且外部一直沒有調(diào)用該實(shí)例,那么這部分的系統(tǒng)資源的消耗是沒有意義的。


例子:


publicclassSingleton{
//首先自己在內(nèi)部定義自己的一個(gè)實(shí)例,只供內(nèi)部調(diào)用
privatestaticfinalSingletoninstance=newSingleton();
//私有構(gòu)造函數(shù)
 privateSingleton(){
}
//提供了靜態(tài)方法,外部可以直接調(diào)用
publicstaticSingletongetInstance(){
returninstance;
}
}
下面模擬單例模式在多線程下會(huì)出現(xiàn)的問題
/**
*懶漢式單例類
*/
publicclassLazySingleton{
//為了易于模擬多線程下,懶漢式出現(xiàn)的問題,我們?cè)趧?chuàng)建實(shí)例的構(gòu)造函數(shù)里面使當(dāng)前線程暫停了50毫秒
privateLazySingleton(){
try{
Thread.sleep(50);
}catch(InterruptedExceptione){
e.printStackTrace();
}
System.out.println("生成LazySingleton實(shí)例一次!");
}
privatestaticLazySingletonlazyInstance=null;
publicstaticLazySingletongetLazyInstance(){
if(lazyInstance==null){
lazyInstance=newLazySingleton();
}
returnlazyInstance;
}
}


測(cè)試代碼:我們?cè)跍y(cè)試代碼里面新建了10個(gè)線程,讓這10個(gè)線程同時(shí)調(diào)用LazySingleton.getLazyInstance()方法


publicclassSingletonTest{
publicstaticvoidmain(String[]args){
 //創(chuàng)建十個(gè)線程調(diào)
for(inti=0;i<10;i++){
newThread(){
@Override
publicvoidrun(){
LazySingleton.getLazyInstance();
}
}.start();
}
}
}


結(jié)果:


生成LazySingleton實(shí)例一次!
生成LazySingleton實(shí)例一次!
生成LazySingleton實(shí)例一次!
生成LazySingleton實(shí)例一次!
生成LazySingleton實(shí)例一次!
生成LazySingleton實(shí)例一次!
生成LazySingleton實(shí)例一次!
生成LazySingleton實(shí)例一次!
生成LazySingleton實(shí)例一次!
生成LazySingleton實(shí)例一次!


可以看出單例模式懶漢式在多線程的并發(fā)下也會(huì)出現(xiàn)問題。


分析一下:多個(gè)線程同時(shí)訪問上面的懶漢式單例,現(xiàn)在有兩個(gè)線程A和B同時(shí)訪問LazySingleton.getLazyInstance()方法。


假設(shè)A先得到CPU的時(shí)間切片,A執(zhí)行到if(lazyInstance==null)時(shí),由于lazyInstance之前并沒有實(shí)例化,所以lazyInstance==null為true,在還沒有執(zhí)行實(shí)例創(chuàng)建的時(shí)候。


此時(shí)CPU將執(zhí)行時(shí)間分給了線程B,線程B執(zhí)行到if(lazyInstance==null)時(shí),由于lazyInstance之前并沒有實(shí)例化,所以lazyInstance==null為true,線程B繼續(xù)往下執(zhí)行實(shí)例的創(chuàng)建過程,線程B創(chuàng)建完實(shí)例之后,返回。


此時(shí)CPU將時(shí)間切片分給線程A,線程A接著開始執(zhí)行實(shí)例的創(chuàng)建,實(shí)例創(chuàng)建完之后便返回。由此看線程A和線程B分別創(chuàng)建了一個(gè)實(shí)例(存在2個(gè)實(shí)例了),這就導(dǎo)致了單例的失效。


解決辦法:我們可以在getLazyInstance方法上加上synchronized使其同步,但是這樣一來,會(huì)降低整個(gè)訪問的速度,而且每次都要判斷。


那么有沒有更好的方式來實(shí)現(xiàn)呢?我們可以考慮使用"雙重檢查加鎖"的方式來實(shí)現(xiàn),就可以既實(shí)現(xiàn)線程安全,又能夠使性能不受到很大的影響。我們看看具體解決代碼:


publicclassLazySingleton{
privateLazySingleton(){
try{
Thread.sleep(50);
}catch(InterruptedExceptione){
e.printStackTrace();
}
System.out.println("生成LazySingleton實(shí)例一次!");
}
privatestaticLazySingletonlazyInstance=null;
publicstaticLazySingletongetLazyInstance(){
 //先檢查實(shí)例是否存在,如果不存在才進(jìn)入下面的同步塊
if(lazyInstance==null){
 //同步塊,線程安全地創(chuàng)建實(shí)例
 synchronized(LazySingleton.class){
 //再次檢查實(shí)例是否存在,如果不存在才真正地創(chuàng)建實(shí)例
 if(lazyInstance==null){
  lazyInstance=newLazySingleton();
  }
 }
}
returnlazyInstance;
}
}


這樣我們就可以在多線程并發(fā)下安全應(yīng)用單例模式中的懶漢模式。這種方法在代碼上可能就不怎么美觀,我們可以優(yōu)雅的使用一個(gè)內(nèi)部類來維護(hù)單例類的實(shí)例,下面看看代碼:


publicclassGracefulSingleton{
privateGracefulSingleton(){
System.out.println("創(chuàng)建GracefulSingleton實(shí)例一次!");
}
//類級(jí)的內(nèi)部類,也就是靜態(tài)的成員式內(nèi)部類,該內(nèi)部類的實(shí)例與外部類的實(shí)例沒有綁定關(guān)系,而且只有被調(diào)用到才會(huì)裝載,從而實(shí)現(xiàn)了延遲加載
privatestaticclassSingletonHoder{
    //靜態(tài)初始化器,由JVM來保證線程安全
privatestaticGracefulSingletoninstance=newGracefulSingleton();
}
publicstaticGracefulSingletongetInstance(){
returnSingletonHoder.instance;
}
}


說一下在實(shí)際開發(fā)中的場(chǎng)景:為了程序的高效率使用多線程并發(fā),然而是循環(huán)調(diào)用,可能導(dǎo)致創(chuàng)建線程數(shù)過多,考慮采用線程池管理,這時(shí)候創(chuàng)建線程池仍然是處于循環(huán)調(diào)用中,也可能導(dǎo)致多個(gè)線程池,這時(shí)候就考慮使用單例模式。


源代碼:


publicclassThreadPoolFactoryUtil{
privateExecutorServiceexecutorService;
 //在構(gòu)造函數(shù)中創(chuàng)建線程池
privateThreadPoolFactoryUtil(){
//獲取系統(tǒng)處理器個(gè)數(shù),作為線程池?cái)?shù)量
intnThreads=Runtime.getRuntime().availableProcessors();
executorService=Executors.newFixedThreadPool(nThreads);
}
//定義一個(gè)靜態(tài)內(nèi)部類,內(nèi)部定義靜態(tài)成員創(chuàng)建外部類實(shí)例
privatestaticclassSingletonContainer{
privatestaticThreadPoolFactoryUtilutil=newThreadPoolFactoryUtil();
}
//獲取本類對(duì)象
publicstaticThreadPoolFactoryUtilgetUtil(){
returnSingletonContainer.util;
}
publicExecutorServicegetExecutorService(){
returnexecutorService;
}
}


涉及到一個(gè)靜態(tài)內(nèi)部類,我們看看靜態(tài)內(nèi)部類的特點(diǎn):


1、靜態(tài)內(nèi)部類無需依賴于外部類,它可以獨(dú)立于外部對(duì)象而存在。
2、靜態(tài)內(nèi)部類,多個(gè)外部類的對(duì)象可以共享同一個(gè)內(nèi)部類的對(duì)象。
3、使用靜態(tài)內(nèi)部類的好處是加強(qiáng)了代碼的封裝性以及提高了代碼的可讀性。
4、普通內(nèi)部類不能聲明static的方法和變量,注意這里說的是變量,常量(也就是finalstatic修飾的屬性)還是可以的,而靜態(tài)內(nèi)部類形似外部類,沒有任何限制??梢灾苯颖挥猛獠款惷?內(nèi)部類名獲得。


想了解更多關(guān)于Java開發(fā)方面內(nèi)容的小伙伴,請(qǐng)關(guān)注扣丁學(xué)堂Java培訓(xùn)官網(wǎng)、微信等平臺(tái),扣丁學(xué)堂IT職業(yè)在線學(xué)習(xí)教育有專業(yè)的Java講師為您指導(dǎo),此外扣丁學(xué)堂老師精心推出的Java視頻教程定能讓你快速掌握J(rèn)ava從入門到精通開發(fā)實(shí)戰(zhàn)技能。扣丁學(xué)堂Java技術(shù)交流群:850353792。


                          JavaEE/微服務(wù)/源碼解析/分布式/企業(yè)級(jí)架構(gòu)【VIP體驗(yàn)課】


     【關(guān)注微信公眾號(hào)獲取更多學(xué)習(xí)資料】        【掃碼進(jìn)入JavaEE/微服務(wù)VIP免費(fèi)公開課】  



查看更多關(guān)于“Java開發(fā)資訊”的相關(guān)文章>>


標(biāo)簽: Java培訓(xùn) Java視頻教程 Java多線程 Java面試題 Java學(xué)習(xí)視頻 springBoot項(xiàng)目

熱門專區(qū)

暫無熱門資訊

課程推薦

微信
微博
15311698296

全國(guó)免費(fèi)咨詢熱線

郵箱:codingke@1000phone.com

官方群:148715490

北京千鋒互聯(lián)科技有限公司版權(quán)所有   北京市海淀區(qū)寶盛北里西區(qū)28號(hào)中關(guān)村智誠(chéng)科創(chuàng)大廈4層
京ICP備2021002079號(hào)-2   Copyright ? 2017 - 2022
返回頂部 返回頂部