Java并發(fā)編程性能詳解-創(chuàng)新互聯(lián)

一、介紹

成都創(chuàng)新互聯(lián)公司專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、民勤網(wǎng)絡(luò)推廣、重慶小程序開發(fā)、民勤網(wǎng)絡(luò)營銷、民勤企業(yè)策劃、民勤品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營等,從售前售中售后,我們都將竭誠為您服務(wù),您的肯定,是我們大的嘉獎(jiǎng);成都創(chuàng)新互聯(lián)公司為所有大學(xué)生創(chuàng)業(yè)者提供民勤建站搭建服務(wù),24小時(shí)服務(wù)熱線:13518219792,官方網(wǎng)址:muchs.cn

本文重點(diǎn)討論多線程應(yīng)用程序的性能問題。如用何種技術(shù)方法來減少鎖競爭,以及如何用代碼來實(shí)現(xiàn)。

二、性能

我們都知道,多線程可以提高線程的性能。性能提升的根本原因在于我們有多核的CPU或多個(gè)CPU。每個(gè)CPU的內(nèi)核都可以自己完成任務(wù),因此把一個(gè)大的任務(wù)分解成一系列的可彼此獨(dú)立運(yùn)行的小任務(wù)就可以提高程序的整體性能了。可以舉個(gè)例子,比如有個(gè)程序用來將硬盤上某個(gè)文件夾下的所有圖片的尺寸進(jìn)行修改,應(yīng)用多線程技術(shù)就可以提高它的性能。使用單線程的方式只能依次遍歷所有圖片文件并且執(zhí)行修改,如果我們的CPU有多個(gè)核心的話,毫無疑問,它只能利用其中的一個(gè)核。使用多線程的方式的話,我們可以讓一個(gè)生產(chǎn)者線程掃描文件系統(tǒng)把每個(gè)圖片都添加到一個(gè)隊(duì)列中,然后用多個(gè)工作線程來執(zhí)行這些任務(wù)。如果我們的工作線程的數(shù)量和CPU總的核心數(shù)一樣的話,我們就能保證每個(gè)CPU核心都有活可干,直到任務(wù)被全部執(zhí)行完成。

對于另外一種需要較多IO等待的程序來說,利用多線程技術(shù)也能提高整體性能。假設(shè)我們要寫這樣一個(gè)程序,需要抓取某個(gè)網(wǎng)站的所有HTML文件,并且將它們存儲到本地磁盤上。程序可以從某一個(gè)網(wǎng)頁開始,然后解析這個(gè)網(wǎng)頁中所有指向本網(wǎng)站的鏈接,然后依次抓取這些鏈接,這樣周而復(fù)始。因?yàn)閺奈覀儗h(yuǎn)程網(wǎng)站發(fā)起請求到接收到所有的網(wǎng)頁數(shù)據(jù)需要等待一段時(shí)間,所以我們可以將此任務(wù)交給多個(gè)線程來執(zhí)行。讓一個(gè)或稍微更多一點(diǎn)的線程來解析已經(jīng)收到的HTML網(wǎng)頁以及將找到的鏈接放入隊(duì)列中,讓其他所有的線程負(fù)責(zé)請求獲取頁面。

高性能就是在短的時(shí)間窗口內(nèi)做盡量多的事情。這個(gè)當(dāng)然是對性能一詞的最經(jīng)典解釋了。但是同時(shí),使用線程也能很好地提升我們程序的響應(yīng)速度。想象我們有這樣一個(gè)圖形界面的應(yīng)用程序,上方有一個(gè)輸入框,輸入框下面有一個(gè)名字叫“處理”的按鈕。當(dāng)用戶按下這個(gè)按鈕的時(shí)候,應(yīng)用程序需要重新對按鈕的狀態(tài)進(jìn)行渲染(按鈕看起來被按下了,當(dāng)松開鼠標(biāo)左鍵時(shí)又恢復(fù)原狀),并且開始對用戶的輸入進(jìn)行處理。如果處理用戶輸入的這個(gè)任務(wù)比較耗時(shí)的話,單線程的程序就無法繼續(xù)響應(yīng)用戶其他的輸入動作了,

可擴(kuò)展性(Scalability)的意思是程序具備這樣的能力:通過添加計(jì)算資源就可以獲得更高的性能。想象我們需要調(diào)整很多圖片的大小,因?yàn)槲覀儥C(jī)器的CPU核心數(shù)是有限的,所以增加線程數(shù)量并不總能相應(yīng)提高性能。相反,因?yàn)檎{(diào)度器需要負(fù)責(zé)更多線程的創(chuàng)建和關(guān)閉,也會占用CPU資源,反而有可能降低性能。

1、對性能的影響

寫到這里,我們已經(jīng)取得這樣一個(gè)觀點(diǎn):增加更多的線程可以提高程序的性能和響應(yīng)速度。但是另一方面,想要取得這些好處卻并非輕而易舉,也需要付出一些代價(jià)。線程的使用對性能的提升也會有所影響。

首先,第一個(gè)影響來自線程創(chuàng)建的時(shí)候。線程的創(chuàng)建過程中,JVM需要從底層操作系統(tǒng)申請相應(yīng)的資源,并且在調(diào)度器中初始化數(shù)據(jù)結(jié)構(gòu),以便決定執(zhí)行線程的順序。

如果你的線程的數(shù)量和CPU的核心數(shù)量一樣的話,每個(gè)線程都會運(yùn)行在一個(gè)核心上,這樣或許他們就不會經(jīng)常被打斷了。但是事實(shí)上,在你的程序運(yùn)行的時(shí)候,操作系統(tǒng)也會有些自己的運(yùn)算需要CPU去處理。所以,即使這種情形下,你的線程也會被打斷并且等待操作系統(tǒng)來重新恢復(fù)它的運(yùn)行。當(dāng)你的線程數(shù)量超過CPU的核心數(shù)量的時(shí)候,情況有可能變得更壞。在這種情況下,JVM的進(jìn)程調(diào)度器會打斷某些線程以便讓其他線程執(zhí)行,線程切換的時(shí)候,剛才正在運(yùn)行的線程的當(dāng)前狀態(tài)需要被保存下來,以便等下次運(yùn)行的時(shí)候可以恢復(fù)數(shù)據(jù)狀態(tài)。不僅如此,調(diào)度器也會對它自己內(nèi)部的數(shù)據(jù)結(jié)構(gòu)進(jìn)行更新,而這也需要消耗CPU周期。所有這些都意味著,線程之間的上下文切換會消耗CPU計(jì)算資源,因此帶來相比單線程情況下沒有的性能開銷。

多線程程序所帶來的另外一個(gè)開銷來自對共享數(shù)據(jù)的同步訪問保護(hù)。我們可以使用synchronized關(guān)鍵字來進(jìn)行同步保護(hù),也可以使用Volatile關(guān)鍵字來在多個(gè)線程之間共享數(shù)據(jù)。如果多于一個(gè)線程想要去訪問某一個(gè)共享數(shù)據(jù)結(jié)構(gòu)的話,就發(fā)生了爭用的情形,這時(shí),JVM需要決定哪個(gè)進(jìn)程先,哪個(gè)進(jìn)程后。如果決定該要執(zhí)行的線程不是當(dāng)前正在運(yùn)行的線程,那么就會發(fā)生線程切換。當(dāng)前線程需要等待,直到它成功獲得了鎖對象。JVM可以自己決定如何來執(zhí)行這種“等待”,假如JVM預(yù)計(jì)離成功獲得鎖對象的時(shí)間比較短,那JVM可以使用激進(jìn)等待方法,比如,不停地嘗試獲得鎖對象,直到成功,在這種情況下這種方式可能會更高效,因?yàn)楸容^進(jìn)程上下文切換來說,還是這種方式更快速一些。把一個(gè)等待狀態(tài)的線程挪回到執(zhí)行隊(duì)列也會帶來額外的開銷。

因此,我們要盡力避免由于鎖競爭而帶來的上下文切換。

下面將具體闡述兩種降低這種競爭發(fā)生的方法。

2、鎖競爭

兩個(gè)或更多線程對鎖的競爭訪問會帶來額外的運(yùn)算開銷,因?yàn)楦偁幍陌l(fā)生逼迫調(diào)度器來讓一個(gè)線程進(jìn)入激進(jìn)等待狀態(tài),或者讓它進(jìn)行等待狀態(tài)而引發(fā)兩次上下文切換。有某些情況下,鎖競爭的惡果可以通過以下方法來減輕:

1.少鎖的作用域;

2.少需要獲取鎖的頻率;

3.量使用由硬件支持的樂觀鎖操作,而不是synchronized;

4.量少用synchronized;

5.少使用對象緩存

2.1 縮減同步域

如果代碼持有鎖超過必要的時(shí)間,那么可以應(yīng)用這第一種方法。通常我們可以將一行或多行代碼移出同步區(qū)域來降低當(dāng)前線程持有鎖的時(shí)間。在同步區(qū)域里運(yùn)行的代碼數(shù)量越少,當(dāng)前線程就會越早地釋放鎖,從而讓其他線程更早地獲得鎖。這與Amdahl法則相一致的,因?yàn)檫@樣做減少了需要同步執(zhí)行的代碼量。

2.2 分拆鎖

另外一種減少鎖競爭的方法是將一塊被鎖定保護(hù)的代碼分散到多個(gè)更小的保護(hù)塊中。如果你的程序中使用了一個(gè)鎖來保護(hù)多個(gè)不同對象的話,這種方式會有用武之地。假設(shè)我們想要通過程序來統(tǒng)計(jì)一些數(shù)據(jù),并且實(shí)現(xiàn)了一個(gè)簡單的計(jì)數(shù)類來持有多個(gè)不同的統(tǒng)計(jì)指標(biāo),并且分別用一個(gè)基本計(jì)數(shù)變量來表示(long類型)。因?yàn)槲覀兊某绦蚴嵌嗑€程的,所以我們需要對訪問這些變量的操作進(jìn)行同步保護(hù),因?yàn)檫@些操作動作來自不同的線程。要達(dá)到這個(gè)目的,最簡單的方式就是對每個(gè)訪問了這些變量的函數(shù)添加synchronized關(guān)鍵字。

2.3 分離鎖

上面一個(gè)例子展示了如何將一個(gè)單獨(dú)的鎖分開為多個(gè)單獨(dú)的鎖,這樣使得各線程僅僅獲得他們將要修改的對象的鎖就可以了。但是另一方面,這種方式也增加了程序的復(fù)雜度,如果實(shí)現(xiàn)不恰當(dāng)?shù)脑捯部赡茉斐伤梨i。

分離鎖是與分拆鎖類似的一種方法,但是分拆鎖是增加鎖來保護(hù)不同的代碼片段或?qū)ο?,而分離鎖是使用不同的鎖來保護(hù)不同范圍的數(shù)值。JDK的java.util.concurrent包里的ConcurrentHashMap即使用了這種思想來提高那些嚴(yán)重依賴HashMap的程序的性能。在實(shí)現(xiàn)上,ConcurrentHashMap內(nèi)部使用了16個(gè)不同的鎖,而不是封裝一個(gè)同步保護(hù)的HashMap。16個(gè)鎖每一個(gè)負(fù)責(zé)保護(hù)其中16分之一的桶位(bucket)的同步訪問。這樣一來,不同的線程想要向不同的段插入鍵的時(shí)候,相應(yīng)的操作會受到不同的鎖來保護(hù)。但是反過來也會帶來一些不好的問題,比如,某些操作的完成現(xiàn)在需要獲取多個(gè)鎖而不是一個(gè)鎖。如果你想要復(fù)制整個(gè)Map的話,這16個(gè)鎖都需要獲得才能完成。

2.4 原子操作

另外一種減少鎖競爭的方法是使用原子操作。java.util.concurrent包對一些常用基礎(chǔ)數(shù)據(jù)類型提供了原子操作封裝的類。原子操作類的實(shí)現(xiàn)基于處理器提供的“比較置換”功能(CAS),CAS操作只在當(dāng)前寄存器的值跟操作提供的舊的值一樣的時(shí)候才會執(zhí)行更新操作。

這個(gè)原理可以用來以樂觀的方式來增加一個(gè)變量的值。如果我們的線程知道當(dāng)前的值的話,就會嘗試使用CAS操作來執(zhí)行增加操作。如果期間別的線程已經(jīng)修改了變量的值,那么線程提供的所謂的當(dāng)前值已經(jīng)跟真實(shí)的值不一樣了,這時(shí)JVM來嘗試重新獲得當(dāng)前值,并且再嘗試一次,反反復(fù)復(fù)直到成功為止。雖然循環(huán)操作會浪費(fèi)一些CPU周期,但是這樣做的好處是,我們不需要任何形式的同步控制。

2.5 避免熱點(diǎn)代碼段

一個(gè)典型的LIST實(shí)現(xiàn)通過會在內(nèi)容維護(hù)一個(gè)變量來記錄LIST自身所包含的元素個(gè)數(shù),每一次從列表里刪除或增加元素的時(shí)候,這個(gè)變量的值都會改變。如果LIST在單線程應(yīng)用中使用的話,這種方式無可厚非,每次調(diào)用size()時(shí)直接返回上一次計(jì)算之后的數(shù)值就行了。如果LIST內(nèi)部不維護(hù)這個(gè)計(jì)數(shù)變量的話,每次調(diào)用size()操作都會引發(fā)LIST重新遍歷計(jì)算元素個(gè)數(shù)。

這種很多數(shù)據(jù)結(jié)構(gòu)都使用了的優(yōu)化方式,到了多線程環(huán)境下時(shí)卻會成為一個(gè)問題。假設(shè)我們在多個(gè)線程之間共享一個(gè)LIST,多個(gè)線程同時(shí)地去向LIST里面增加或刪除元素,同時(shí)去查詢大的長度。這時(shí),LIST內(nèi)部的計(jì)數(shù)變量成為一個(gè)共享資源,因此所有對它的訪問都必須進(jìn)行同步處理。因此,計(jì)數(shù)變量成為整個(gè)LIST實(shí)現(xiàn)中的一個(gè)熱點(diǎn)。

本文所講述的這些優(yōu)化方案再一次的表明,每一種優(yōu)化方式在真正應(yīng)用的時(shí)候一定需要多多仔細(xì)觀測。不成熟的優(yōu)化方案表面看起來好像很有道理,但是事實(shí)上很有可能會反過來成為性能的瓶頸。

創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動態(tài)BGP最優(yōu)骨干路由自動選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動現(xiàn)已開啟,新人活動云服務(wù)器買多久送多久。

網(wǎng)頁題目:Java并發(fā)編程性能詳解-創(chuàng)新互聯(lián)
當(dāng)前網(wǎng)址:http://muchs.cn/article36/deicpg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計(jì)、自適應(yīng)網(wǎng)站、全網(wǎng)營銷推廣、電子商務(wù)品牌網(wǎng)站建設(shè)、網(wǎng)站改版

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)