使用Java中成型的框架來幫助我們開發(fā)并發(fā)應(yīng)用即可以節(jié)省構(gòu)建項目的時間,也可以提高應(yīng)用的性能。
創(chuàng)新互聯(lián)專注于亞東網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供亞東營銷型網(wǎng)站建設(shè),亞東網(wǎng)站制作、亞東網(wǎng)頁設(shè)計、亞東網(wǎng)站官網(wǎng)定制、微信小程序開發(fā)服務(wù),打造亞東網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供亞東網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。Java對象實例的鎖一共有四種狀態(tài):無鎖,偏向鎖,輕量鎖和重量鎖。原始脫離框架的并發(fā)應(yīng)用大部分都需要手動完成加鎖釋放,最直接的就是使用synchronized和volatile關(guān)鍵字對某個對象或者代碼塊加鎖從而限制每次訪問的次數(shù),從對象之間的競爭也可以實現(xiàn)到對象之間的協(xié)作。但是這樣手動實現(xiàn)出來的應(yīng)用不僅耗費時間而且性能表現(xiàn)往往又有待提升。順帶一提,之前寫過一篇文章介紹我基于Qt和Linux實現(xiàn)的一個多線程下載器(到這里不需要更多了解這個下載器,請直接繼續(xù)閱讀),就拿這個下載器做一次反例:
首先,一個下載器最愚蠢的問題之一就是把下載線程的個數(shù)交由給用戶去配置。比如一個用戶會認(rèn)為負(fù)責(zé)下載的線程個數(shù)是越多越好,干脆配置了50個線程去下載一份任務(wù),那么這個下載器的性能表現(xiàn)甚至?xí)蝗缫粋€單進程的下載程序。最直接的原因就是JVM花費了很多計算資源在線程之間的上下文切換上面,對于一個并發(fā)的應(yīng)用:如果是CPU密集型的任務(wù),那么良好的線程個數(shù)是實際CPU處理器的個數(shù)的1倍;如果是I/O密集型的任務(wù),那么良好的線程個數(shù)是實際CPU處理器個數(shù)的1.5倍到2倍(具體記不清這句話是出于哪里了,但還是可信的)。不恰當(dāng)?shù)膱?zhí)行線程個數(shù)會給線程抖動,CPU抖動等隱患埋下伏筆。如果,重新開發(fā)那么我一定會使用這種線程池的方法使用生產(chǎn)者和消費者的關(guān)系模式,異步處理HTTP傳輸過來的報文。
其次,由于HTTP報文的接受等待的時間可能需要等待很久,然而處理報文解析格式等等消耗的計算資源是相當(dāng)較小的。同步地處理這兩件事情必然會使下載進程在一段時間內(nèi)空轉(zhuǎn)或者阻塞,這樣處理也是非常不合理的。如果重新開發(fā),一定要解耦HTTP報文的接收和HTTP報文的解析,這里盡管也可以使用線程池去進行處理,顯而易見由于這樣去做的性能提升其實是很小的,所以沒有必要去實現(xiàn),單線程也可以快速完成報文的解析。
Okay,回到主題,總而言之是線程之間的上下文切換導(dǎo)致了性能的降低。那么具體應(yīng)該怎么樣去做才可以減少上下文的切換呢?
1. 無鎖并發(fā)編程
多線程競爭鎖時,會引起上下文切換,所以多線程處理數(shù)據(jù)時,可以用一些辦法來避免使用鎖,如將數(shù)據(jù)的ID按照Hash算法取模分段,不同的線程去處理不同段的數(shù)據(jù)。
2. CAS算法
Java的Atomic包內(nèi)使用CAS算法來更新數(shù)據(jù),而不需要加鎖(但是線程的空轉(zhuǎn)還是存在)。
3. 使用最少線程
避免創(chuàng)建不需要的線程,比如任務(wù)很少,但是創(chuàng)建很多線程來處理,這樣會造成大量線程都處于等待狀態(tài)。
4. 協(xié)程
在單線程里實現(xiàn)多任務(wù)的調(diào)度,并在單線程里維持多個任務(wù)間的切換。
總的來說使用Java線程池會帶來以下3個好處:
1. 降低資源消耗: 通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
2. 提高響應(yīng)速度: 當(dāng)任務(wù)到達時,任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行。
3. 提高線程的可管理性: 線程是稀缺資源,如果無限制的創(chuàng)建。不僅僅會降低系統(tǒng)的穩(wěn)定性,使用線程池可以統(tǒng)一分配,調(diào)優(yōu)和監(jiān)控。但是要做到合理的利用線程池。必須對于其實現(xiàn)原理了如指掌。
線程池的實現(xiàn)原理如下圖所示:
Executor框架的兩級調(diào)度模型:
在HotSpot VM線程模型中,Java線程被一對一的映射為本地操作系統(tǒng)線程,Java線程啟動時會創(chuàng)建一個本地操作系統(tǒng)線程,當(dāng)該Java線程終止時,這個操作系統(tǒng)也會被回收。操作系統(tǒng)會調(diào)度并將它們分配給可用的CPU。
在上層,Java多線程程序通常把應(yīng)用分解為若干個任務(wù),然后把用戶級的調(diào)度器(Executor框架)將這些映射為固定數(shù)量的線程;在底層,操作系統(tǒng)內(nèi)核將這些線程映射到硬件處理器上。這種兩級調(diào)度模型實質(zhì)是一種工作單元和執(zhí)行機制的解偶。
Fork/Join框架的遞歸調(diào)度模型:
要提高應(yīng)用程序在多核處理器上的執(zhí)行效率,只能想辦法提高應(yīng)用程序的本身的并行能力。常規(guī)的做法就是使用多線程,讓更多的任務(wù)同時處理,或者讓一部分操作異步執(zhí)行,這種簡單的多線程處理方式在處理器核心數(shù)比較少的情況下能夠有效地利用處理資源,因為在處理器核心比較少的情況下,讓不多的幾個任務(wù)并行執(zhí)行即可。但是當(dāng)處理器核心數(shù)發(fā)展很大的數(shù)目,上百上千的時候,這種按任務(wù)的并發(fā)處理方法也不能充分利用處理資源,因為一般的應(yīng)用程序沒有那么多的并發(fā)處理任務(wù)(服務(wù)器程序是個例外)。所以,只能考慮把一個任務(wù)拆分為多個單元,每個單元分別得執(zhí)行最后合并每個單元的結(jié)果。一個任務(wù)的并行拆分,一種方法就是寄希望于硬件平臺或者操作系統(tǒng),但是目前這個領(lǐng)域還沒有很好的結(jié)果。另一種方案就是還是只有依靠應(yīng)用程序本身對任務(wù)經(jīng)行拆封執(zhí)行。
Fork/Join模型乍看起來很像借鑒了MapReduce,但是具體不敢肯定是什么原因,實際用起來的性能提升是遠不如Executor的。甚至在遞歸棧到了十層以上的時候,JVM會卡死或者崩潰,從計算機的物理原理來看,F(xiàn)ork/Join框架實際效能也沒有想象中的那么美好,所以這篇只稍微談一下,不再深究。
Executor框架主要由三個部分組成:任務(wù),任務(wù)的執(zhí)行,異步計算的結(jié)果。
主要的類和接口簡介如下:
1. Executor是一個接口,它將任務(wù)的提交和任務(wù)的執(zhí)行分離。
2. ThreadPoolExecutor是線程池的核心,用來執(zhí)行被提交的類。
3. Future接口和實現(xiàn)Future接口的FutureTask類,代表異步計算的結(jié)果。
4. Runnable接口和Callable接口的實現(xiàn)類,都可以被ThreadPoolExecutor或其他執(zhí)行。
先看一個直接的例子(用SingleThreadExecutor來實現(xiàn),具體原理下面會闡述):
1 public class ExecutorDemo { 2 3 4 public static void main(String[] args){ 5 6 //ExecutorService fixed= Executors.newFixedThreadPool(4); 7 ExecutorService single=Executors.newSingleThreadExecutor(); 8 //ExecutorService cached=Executors.newCachedThreadPool(); 9 //ExecutorService sched=Executors.newScheduledThreadPool(4); 11 12 Callablecallable=Executors.callable(new Runnable() { 13 @Override 14 public void run() { 15 for(int i=0;i<100;i++){ 16 try{ 17 System.out.println(i); 18 }catch(Throwable e){ 19 e.printStackTrace(); 20 } 21 } 22 } 23 },"success"); 24 ?????//這里抖了個機靈,用Executors工具類的callable方法將一個匿名Runnable對象裝飾為Callable對象作為參數(shù) 25 Futuref=single.submit(callable); 26 try { 27 System.out.println(f.get()); 28 single.shutdown(); 29 }catch(Throwable e){ 30 e.printStackTrace(); 31 } 32 } 33 }
如代碼中所示,常用一共有四種Exector實現(xiàn)類通過Executors的工廠方法來創(chuàng)建Executor的實例,其具體差別及特點如下所示:
1. FixedThreadPool
這個是我個人最常用的實現(xiàn)類,在Java中最直接的使用方法就是和 Runtime.getRuntime().availableProcessors() 一起使用分配處理器個數(shù)個的Executor。內(nèi)部結(jié)構(gòu)大致如下:
創(chuàng)造實例的函數(shù)為: Executors.newFixedThreadPool(int nThread);
在JDK1.7里java.util.concurrent包中的源碼中隊列使用的是new LinkedBlockingQueue,這是一個×××的隊列,也就是說任務(wù)有可能無限地積壓在這個等待隊列之中,實際使用是存在一定的隱患。但是構(gòu)造起來相當(dāng)比較容易,我個人建議在使用的過程之中不斷查詢size()來保證該阻塞隊列不會無限地生長。
2. SingleThreadExecutor
和 Executors.newFixedThreadPool(1) 完全等價。
3. CachedThreadPool
和之前兩個實現(xiàn)類完全不同的是,這里使用SynchronousQueue替換LinkedBlockingQueue。簡單提一下SynchronousQueue是一個沒有容量的隊列,一個offer必須對應(yīng)一個poll,當(dāng)然所謂poll操作是由實際JVM工作線程來進行的,所以對于使用開發(fā)者來講,這是一個會因為工作線程飽和而阻塞的線程池。(這個和java.util.concurrent.Exchanger的作用有些相似,但是Exchanger只是對于兩個JVM線程的,而SynchronousQueue的阻塞機制是多個生產(chǎn)者和多個消費者而言的。)
4. ScheduledThreadPoolExecutor
這個實現(xiàn)類內(nèi)部使用的是DelayQueue。DelayQueue實際上是一個優(yōu)先級隊列的封裝。時間早的任務(wù)會擁有更高的優(yōu)先級。它主要用來在給定的延遲之后運行任務(wù),或者定期執(zhí)行任務(wù)。ScheduledThreadPoolExecutor的功能與Timer類似,但ScheduledThreadPoolExecutor比Timer更加靈活,而且可以有多個后臺線程在構(gòu)造函數(shù)之中指定。
Future接口和ListenableFurture接口
Future接口為異步計算取回結(jié)果提供了一個存根(stub),然而這樣每次調(diào)用Future接口的get方法取回計算結(jié)果往往是需要面臨阻塞的可能性。這樣在最壞的情況下,異步計算和同步計算的消耗是一致的。Guava庫中因此提供一個非常強大的裝飾后的Future接口,使用觀察者模式為在異步計算完成之后馬上執(zhí)行addListener指定一個Runnable對象,從實現(xiàn)“完成立即通知”。
創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動態(tài)BGP最優(yōu)骨干路由自動選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機房獨有T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動現(xiàn)已開啟,新人活動云服務(wù)器買多久送多久。
文章題目:關(guān)于并發(fā)框架Java原生線程池原理及Guava與之的補充-創(chuàng)新互聯(lián)
本文URL:http://www.rwnh.cn/article2/cehsic.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供靜態(tài)網(wǎng)站、App設(shè)計、網(wǎng)站制作、定制網(wǎng)站、建站公司、響應(yīng)式網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容