本篇內(nèi)容主要講解“怎么理解Java并發(fā)隊(duì)列與容器”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“怎么理解Java并發(fā)隊(duì)列與容器”吧!
成都創(chuàng)新互聯(lián)是一家網(wǎng)站設(shè)計(jì)公司,集創(chuàng)意、互聯(lián)網(wǎng)應(yīng)用、軟件技術(shù)為一體的創(chuàng)意網(wǎng)站建設(shè)服務(wù)商,主營(yíng)產(chǎn)品:成都響應(yīng)式網(wǎng)站建設(shè)、品牌網(wǎng)站制作、網(wǎng)絡(luò)營(yíng)銷推廣。我們專注企業(yè)品牌在網(wǎng)站中的整體樹(shù)立,網(wǎng)絡(luò)互動(dòng)的體驗(yàn),以及在手機(jī)等移動(dòng)端的優(yōu)質(zhì)呈現(xiàn)。成都做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、移動(dòng)互聯(lián)產(chǎn)品、網(wǎng)絡(luò)運(yùn)營(yíng)、VI設(shè)計(jì)、云產(chǎn)品.運(yùn)維為核心業(yè)務(wù)。為用戶提供一站式解決方案,我們深知市場(chǎng)的競(jìng)爭(zhēng)激烈,認(rèn)真對(duì)待每位客戶,為客戶提供賞析悅目的作品,網(wǎng)站的價(jià)值服務(wù)。
BlockingQueue
阻塞隊(duì)列,位于java.util.concurrent并發(fā)包下,它很好的解決了多線程中如何安全、高效的數(shù)據(jù)傳輸問(wèn)題。所謂“阻塞”是指在某些情況下線程被掛起,當(dāng)滿足一定條件時(shí)會(huì)被自動(dòng)喚醒,可以通過(guò)API進(jìn)行控制。
常見(jiàn)的阻塞隊(duì)列主要分為兩種FIFO(先進(jìn)先出)和LIFO(后進(jìn)先出),當(dāng)然通過(guò)不同的實(shí)現(xiàn)方式,還可以引申出多種不同類型的隊(duì)列。首先了解一下BlockingQueue的幾個(gè)核心API:put、take一對(duì)阻塞存?。籥dd、poll一對(duì)非阻塞存取。put(anObj):把a(bǔ)nObj加到BlockingQueue里,如果BlockQueue沒(méi)有空間,則調(diào)用此方法的線程被阻塞,直到BlockingQueue里面有空間再繼續(xù)插入add(anObj):把a(bǔ)nObj加到BlockingQueue里,如果BlockingQueue可以容納,則返回true,否則拋出異常offer(anObj):表示如果可能的話,將anObj加到BlockingQueue里,如果BlockingQueue可以容納,則返回true,否則返回false。take():取走BlockingQueue里排在首位的對(duì)象,若BlockingQueue為空,阻斷進(jìn)入等待狀態(tài),直到Blocking有新的對(duì)象被加入為止
poll(time):取走BlockingQueue里排在首位的對(duì)象,若不能立即取出,則可以等time參數(shù)規(guī)定的時(shí)間,取不到時(shí)返回null
基于數(shù)組實(shí)現(xiàn)的有界阻塞隊(duì)列。因?yàn)榛跀?shù)組實(shí)現(xiàn),所以具有查找快,增刪慢的特點(diǎn)。生產(chǎn)者和消費(fèi)者用的是同一把鎖,不能并行執(zhí)行效率低。它底層使用了一種標(biāo)準(zhǔn)互斥鎖ReentrantLock,即讀讀、讀寫(xiě),寫(xiě)寫(xiě)都互斥,當(dāng)然可以控制對(duì)象內(nèi)部是否采用公平鎖,默認(rèn)是非公平鎖。消費(fèi)方式是FIFO。
生產(chǎn)和消費(fèi)數(shù)據(jù)時(shí),直接將枚舉對(duì)象插入或刪除,不會(huì)產(chǎn)生或銷毀額外的對(duì)象實(shí)例。應(yīng)用:因?yàn)榈讓由a(chǎn)和消費(fèi)用了同一把鎖,定長(zhǎng)數(shù)組不用頻繁創(chuàng)建和銷毀對(duì)象,適合于想按照隊(duì)列順序去執(zhí)行任務(wù),還不想出現(xiàn)頻繁的GC的場(chǎng)景。基于鏈表實(shí)現(xiàn)的阻塞隊(duì)列,同樣具有增刪快,定位慢的特點(diǎn)。需要注意一點(diǎn):默認(rèn)情況下創(chuàng)建的LinkedBlockingQueue容量是Integer.MAX_VALUE, 在這種情況下,如果生產(chǎn)者的速度一旦大于消費(fèi)者的速度,可能還沒(méi)有等到隊(duì)列滿阻塞產(chǎn)生,系統(tǒng)內(nèi)存就有可能已被消耗盡??梢酝ㄟ^(guò)指定容量創(chuàng)建LinkedBlockingQueue避免這種極端情況的發(fā)生。雖然底層使用的也是ReentrantLock但take和put是分離的(生產(chǎn)和消費(fèi)的鎖不是同一把鎖),高并發(fā)場(chǎng)景下效率仍然高于ArrayBlockingQueue。put方法在隊(duì)列滿的時(shí)候會(huì)阻塞直到有隊(duì)列成員被消費(fèi),take方法在隊(duì)列空的時(shí)候會(huì)阻塞,直到有隊(duì)列成員被放進(jìn)來(lái)。DelayQueue是一個(gè)沒(méi)有大小限制的隊(duì)列,因此往隊(duì)列中插入數(shù)據(jù)的操作(生產(chǎn)者)永遠(yuǎn)不會(huì)被阻塞,而只有獲取數(shù)據(jù)的操作(消費(fèi)者)才會(huì)被阻塞。DelayQueue中的元素,只有指定的延遲時(shí)間到了,才能夠從隊(duì)列中獲取到該元素。
1.客戶端長(zhǎng)時(shí)間占用連接的問(wèn)題,超過(guò)這個(gè)空閑時(shí)間了,可以移除的2.處理長(zhǎng)時(shí)間不用的緩存:如果隊(duì)列里面的對(duì)象長(zhǎng)時(shí)間不用,超過(guò)空閑時(shí)間,就移除3.任務(wù)超時(shí)處理
PriorityBlockingQueue不會(huì)阻塞數(shù)據(jù)生產(chǎn)者,而只會(huì)在沒(méi)有可消費(fèi)的數(shù)據(jù)時(shí),阻塞數(shù)據(jù)的消費(fèi)者。因此必須控制生產(chǎn)者生產(chǎn)數(shù)據(jù)的速度,避免消費(fèi)者消費(fèi)數(shù)據(jù)速度跟不上,否則時(shí)間一長(zhǎng),會(huì)最終耗盡所有的可用堆內(nèi)存空間。在向PriorityBlockingQueue中添加元素時(shí),元素通過(guò)在實(shí)現(xiàn)實(shí)現(xiàn)Comparable接口,重寫(xiě)compareTo()來(lái)定義優(yōu)先級(jí)的邏輯。它內(nèi)部控制線程同步的鎖采用的是公平鎖。一種無(wú)緩沖的等待隊(duì)列,來(lái)一個(gè)任務(wù)就執(zhí)行這個(gè)任務(wù),這期間不能添加任何的任務(wù)。也就是不用阻塞了,其實(shí)對(duì)于少量任務(wù)而言,這種做法更高效。聲明一個(gè)SynchronousQueue有兩種不同的方式,公平模式和非公平模式:
公平模式:SynchronousQueue會(huì)采用公平鎖,并配合一個(gè)FIFO隊(duì)列來(lái)阻塞多余的生產(chǎn)者和消費(fèi)者,從而體現(xiàn)整體的公平策略;非公平模式(SynchronousQueue默認(rèn)):SynchronousQueue采用非公平鎖,同時(shí)配合一個(gè)LIFO隊(duì)列來(lái)管理多余的生產(chǎn)者和消費(fèi)者,而后一種模式,如果生產(chǎn)者和消費(fèi)者的處理速度有差距,則很容易出現(xiàn)饑渴的情況,即可能有某些生產(chǎn)者或者是消費(fèi)者的數(shù)據(jù)永遠(yuǎn)都得不到處理。不上鎖,高并發(fā)場(chǎng)景效率遠(yuǎn)高于ArrayBlockingQueue和LinkedBlockingQueue等第一類:Vector、Stack、HashTable都是同步類,線程安全的,但高并發(fā)場(chǎng)景下仍然可能出現(xiàn)問(wèn)題如ConcurrentModificationException。第二類:Collections提供的一些工廠類(靜態(tài)),效率低
并發(fā)類容器
寫(xiě)時(shí)復(fù)制的容器:當(dāng)我們往一個(gè)容器添加元素的時(shí)候,不直接往當(dāng)前容器添加,而是先將當(dāng)前容器進(jìn)行copy,復(fù)制出一個(gè)新的容器,然后往新的容器里添加元素,添加完元素之后,再將原容器的引用指向新的容器,非常適合讀多寫(xiě)少的場(chǎng)景。但同時(shí)存在如下問(wèn)題:數(shù)據(jù)一致性問(wèn)題:CopyOnWrite容器是弱一致性的,即只能保證數(shù)據(jù)的最終一致性,不能保證數(shù)據(jù)的實(shí)時(shí)一致性。所以如果你希望寫(xiě)入的的數(shù)據(jù)能夠即時(shí)讀到,不要使用CopyOnWrite容器。內(nèi)存占用問(wèn)題:因?yàn)镃opyOnWrite 的寫(xiě)時(shí)復(fù)制機(jī)制,所以在進(jìn)行寫(xiě)操作的時(shí)候,內(nèi)存里會(huì)同時(shí)駐扎兩個(gè)對(duì)象的內(nèi)存,舊的對(duì)象和新寫(xiě)入的對(duì)象。如果這些對(duì)象占用的內(nèi)存比較大,如果控制不好,比如寫(xiě)特別多的情景,很有可能造成頻繁的Yong GC 和Full GC。針對(duì)內(nèi)存占用問(wèn)題,可以通過(guò)壓縮容器中的元素的方法來(lái)減少大對(duì)象的內(nèi)存消耗,或者不使用CopyOnWrite容器,而使用其他的并發(fā)容器,如ConcurrentHashMap。有兩種常見(jiàn)的CopyOnWrite容器:CopyOnWriteArrayList和CopyOnWriteArraySet,其中CopyOnWriteArrayList是ArrayList 的一個(gè)線程安全的變體。
筆者分JDK1.7和JDK1.8兩部分說(shuō)明ConcurrentHashMap。JDK1.7采用"鎖分段"技術(shù)來(lái)降低鎖的粒度,它把整個(gè)map劃分為一系列由segment組成的單元,一個(gè)segment相當(dāng)于一個(gè)hashtable。通過(guò)這種方式,加鎖的對(duì)象就從整個(gè)map變成了一個(gè)segment。ConcurrentHashMap線程安全并且提高性能原因就在于:對(duì)map中的讀是并發(fā)的,無(wú)需加鎖;只有在put、remove操作時(shí)才加鎖,而加鎖僅是對(duì)需要操作的segment加鎖,不會(huì)影響其他segment的讀寫(xiě)。因此不同的segment之間可以并發(fā)使用,極大地提高了性能。根據(jù)源碼又可得出查找、插入、刪除的過(guò)程:通過(guò)key的hash確定segement(插入時(shí)如果segment大小達(dá)到擴(kuò)容閾值則進(jìn)行擴(kuò)容) --> 確定鏈表數(shù)組HashEntry下標(biāo)(插入/刪除時(shí),獲取鏈表頭) --> 遍歷鏈表【查詢:調(diào)用equals()進(jìn)行比對(duì),找到與所查找key相等的結(jié)點(diǎn)并讀??;插入:如果找到相同的key的結(jié)點(diǎn)則更新value值,如果沒(méi)有則插入新結(jié)點(diǎn);刪除:找到被刪除結(jié)點(diǎn)后,以被刪除結(jié)點(diǎn)的next結(jié)點(diǎn)開(kāi)始建立新的鏈表,然后再把原鏈表頭直到被刪結(jié)點(diǎn)的前繼結(jié)點(diǎn)依次復(fù)制、插入新鏈表,最后把新鏈表頭設(shè)置為當(dāng)前數(shù)組下標(biāo)元素取代舊鏈表。
JDK1.8中的ConcurrentHashMap在JDK1.7上做了很多優(yōu)化:1.取消segments字段,直接采用transient volatile HashEntry<K,V>[] table保存數(shù)據(jù),采用table數(shù)組元素作為鎖,從而實(shí)現(xiàn)了對(duì)每一行數(shù)據(jù)進(jìn)行加鎖,通過(guò)進(jìn)一步降低鎖粒度來(lái)減少并發(fā)沖突的概率2.將原先table數(shù)組+鏈表的數(shù)據(jù)結(jié)構(gòu),變更為table數(shù)組+鏈表+紅黑樹(shù)的結(jié)構(gòu)。對(duì)于hash表來(lái)說(shuō),最核心的能力在于將key hash之后能均勻的分布在數(shù)組中。如果hash之后散列的很均勻,那么table數(shù)組中的每個(gè)隊(duì)列長(zhǎng)度主要為0或者1。但實(shí)際情況并非總是如此理想,雖然ConcurrentHashMap類默認(rèn)的加載因子為0.75,但是在數(shù)據(jù)量過(guò)大或者運(yùn)氣不佳的情況下,還是會(huì)存在一些隊(duì)列長(zhǎng)度過(guò)長(zhǎng)的情況,如果還是采用單向列表方式,那么查詢某個(gè)節(jié)點(diǎn)的時(shí)間復(fù)雜度為O(n);因此,對(duì)于個(gè)數(shù)超過(guò)8(默認(rèn)值)的列表,jdk1.8中采用了紅黑樹(shù)的結(jié)構(gòu),那么查詢的時(shí)間復(fù)雜度可以降低到O(logN),可以改進(jìn)性能3.新增字段transient volatile CounterCell[] counterCells,可方便的計(jì)算集合中所有元素的個(gè)數(shù),性能大大優(yōu)于jdk1.7中的size()到此,相信大家對(duì)“怎么理解Java并發(fā)隊(duì)列與容器”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
分享標(biāo)題:怎么理解Java并發(fā)隊(duì)列與容器
網(wǎng)頁(yè)地址:http://redsoil1982.com.cn/article18/jhedgp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供建站公司、虛擬主機(jī)、關(guān)鍵詞優(yōu)化、定制開(kāi)發(fā)、電子商務(wù)、微信小程序
廣告
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源:
創(chuàng)新互聯(lián)