透徹講解:并發(fā)編程的優(yōu)缺點(diǎn)

本人免費(fèi)整理了Java高級(jí)資料,涵蓋了Java、redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并發(fā)分布式等教程,一共30G,需要自己領(lǐng)取。

站在用戶(hù)的角度思考問(wèn)題,與客戶(hù)深入溝通,找到瑞金網(wǎng)站設(shè)計(jì)與瑞金網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶(hù)體驗(yàn)好的作品,建站類(lèi)型包括:成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、空間域名、虛擬空間、企業(yè)郵箱。業(yè)務(wù)覆蓋瑞金地區(qū)。

傳送門(mén):https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q

一直以來(lái)并發(fā)編程對(duì)于剛?cè)胄械男“讈?lái)說(shuō)總是覺(jué)得高深莫測(cè),于是乎,就誕生了想寫(xiě)點(diǎn)東西記錄下,以提升理解和堆并發(fā)編程的認(rèn)知。為什么需要用的并發(fā)?凡事總有好壞兩面,之間的trade-off是什么,也就是說(shuō)并發(fā)編程具有哪些缺點(diǎn)?以及在進(jìn)行并發(fā)編程時(shí)應(yīng)該了解和掌握的概念是什么?這篇文章主要以這三個(gè)問(wèn)題來(lái)談一談。

1. 為什么要用到并發(fā)

一直以來(lái),硬件的發(fā)展極其迅速,也有一個(gè)很著名的"摩爾定律",可能會(huì)奇怪明明討論的是并發(fā)編程為什么會(huì)扯到了硬件的發(fā)展,這其中的關(guān)系應(yīng)該是多核CPU的發(fā)展為并發(fā)編程提供的硬件基礎(chǔ)。摩爾定律并不是一種自然法則或者是物理定律,它只是基于認(rèn)為觀測(cè)數(shù)據(jù)后,對(duì)未來(lái)的一種預(yù)測(cè)。按照所預(yù)測(cè)的速度,我們的計(jì)算能力會(huì)按照指數(shù)級(jí)別的速度增長(zhǎng),不久以后會(huì)擁有超強(qiáng)的計(jì)算能力,正是在暢想未來(lái)的時(shí)候,2004年,Intel宣布4GHz芯片的計(jì)劃推遲到2005年,然后在2004年秋季,Intel宣布徹底取消4GHz的計(jì)劃,也就是說(shuō)摩爾定律的有效性超過(guò)了半個(gè)世紀(jì)戛然而止。但是,聰明的硬件工程師并沒(méi)有停止研發(fā)的腳步,他們?yōu)榱诉M(jìn)一步提升計(jì)算速度,而不是再追求單獨(dú)的計(jì)算單元,而是將多個(gè)計(jì)算單元整合到了一起,也就是形成了多核CPU。短短十幾年的時(shí)間,家用型CPU,比如Intel i7就可以達(dá)到4核心甚至8核心。而專(zhuān)業(yè)服務(wù)器則通常可以達(dá)到幾個(gè)獨(dú)立的CPU,每一個(gè)CPU甚至擁有多達(dá)8個(gè)以上的內(nèi)核。因此,摩爾定律似乎在CPU核心擴(kuò)展上繼續(xù)得到體驗(yàn)。因此,多核的CPU的背景下,催生了并發(fā)編程的趨勢(shì),通過(guò)并發(fā)編程的形式可以將多核CPU的計(jì)算能力發(fā)揮到極致,性能得到提升。

頂級(jí)計(jì)算機(jī)科學(xué)家Donald Ervin Knuth如此評(píng)價(jià)這種情況:在我看來(lái),這種現(xiàn)象(并發(fā))或多或少是由于硬件設(shè)計(jì)者無(wú)計(jì)可施了導(dǎo)致的,他們將摩爾定律的責(zé)任推給了軟件開(kāi)發(fā)者。

另外,在特殊的業(yè)務(wù)場(chǎng)景下先天的就適合于并發(fā)編程。比如在圖像處理領(lǐng)域,一張1024X768像素的圖片,包含達(dá)到78萬(wàn)6千多個(gè)像素。即時(shí)將所有的像素遍歷一邊都需要很長(zhǎng)的時(shí)間,面對(duì)如此復(fù)雜的計(jì)算量就需要充分利用多核的計(jì)算的能力。又比如當(dāng)我們?cè)诰W(wǎng)上購(gòu)物時(shí),為了提升響應(yīng)速度,需要拆分,減庫(kù)存,生成訂單等等這些操作,就可以進(jìn)行拆分利用多線(xiàn)程的技術(shù)完成。面對(duì)復(fù)雜業(yè)務(wù)模型,并行程序會(huì)比串行程序更適應(yīng)業(yè)務(wù)需求,而并發(fā)編程更能吻合這種業(yè)務(wù)拆分?。正是因?yàn)檫@些優(yōu)點(diǎn),使得多線(xiàn)程技術(shù)能夠得到重視,也是一名CS學(xué)習(xí)者應(yīng)該掌握的:

  • 充分利用多核CPU的計(jì)算能力;

  • 方便進(jìn)行業(yè)務(wù)拆分,提升應(yīng)用性能

2. 并發(fā)編程有哪些缺點(diǎn)

多線(xiàn)程技術(shù)有這么多的好處,難道就沒(méi)有一點(diǎn)缺點(diǎn)么,就在任何場(chǎng)景下就一定適用么?很顯然不是。

2.1 頻繁的上下文切換

時(shí)間片是CPU分配給各個(gè)線(xiàn)程的時(shí)間,因?yàn)闀r(shí)間非常短,所以CPU不斷通過(guò)切換線(xiàn)程,讓我們覺(jué)得多個(gè)線(xiàn)程是同時(shí)執(zhí)行的,時(shí)間片一般是幾十毫秒。而每次切換時(shí),需要保存當(dāng)前的狀態(tài)起來(lái),以便能夠進(jìn)行恢復(fù)先前狀態(tài),而這個(gè)切換時(shí)非常損耗性能,過(guò)于頻繁反而無(wú)法發(fā)揮出多線(xiàn)程編程的優(yōu)勢(shì)。通常減少上下文切換可以采用無(wú)鎖并發(fā)編程,CAS算法,使用最少的線(xiàn)程和使用協(xié)程。

  • 無(wú)鎖并發(fā)編程:可以參照concurrentHashMap鎖分段的思想,不同的線(xiàn)程處理不同段的數(shù)據(jù),這樣在多線(xiàn)程競(jìng)爭(zhēng)的條件下,可以減少上下文切換的時(shí)間。

  • CAS算法,利用Atomic下使用CAS算法來(lái)更新數(shù)據(jù),使用了樂(lè)觀鎖,可以有效的減少一部分不必要的鎖競(jìng)爭(zhēng)帶來(lái)的上下文切換

  • 使用最少線(xiàn)程:避免創(chuàng)建不需要的線(xiàn)程,比如任務(wù)很少,但是創(chuàng)建了很多的線(xiàn)程,這樣會(huì)造成大量的線(xiàn)程都處于等待狀態(tài)

  • 協(xié)程:在單線(xiàn)程里實(shí)現(xiàn)多任務(wù)的調(diào)度,并在單線(xiàn)程里維持多個(gè)任務(wù)間的切換

由于上下文切換也是個(gè)相對(duì)比較耗時(shí)的操作,所以在"java并發(fā)編程的藝術(shù)"一書(shū)中有過(guò)一個(gè)實(shí)驗(yàn),并發(fā)累加未必會(huì)比串行累加速度要快。 可以使用Lmbench4測(cè)量上下文切換的時(shí)長(zhǎng)?vmstat測(cè)量上下文切換次數(shù)

2.2 線(xiàn)程安全

多線(xiàn)程編程中最難以把握的就是臨界區(qū)線(xiàn)程安全問(wèn)題,稍微不注意就會(huì)出現(xiàn)死鎖的情況,一旦產(chǎn)生死鎖就會(huì)造成系統(tǒng)功能不可用。

public?class?DeadLockDemo?{????private?static?String?resource_a?=?"A";????private?static?String?resource_b?=?"B";????public?static?void?main(String[]?args)?{
????????deadLock();
????}????public?static?void?deadLock()?{
????????Thread?threadA?=?new?Thread(new?Runnable()?{????????????@Override
????????????public?void?run()?{????????????????synchronized?(resource_a)?{
????????????????????System.out.println("get?resource?a");????????????????????try?{
????????????????????????Thread.sleep(3000);????????????????????????synchronized?(resource_b)?{
????????????????????????????System.out.println("get?resource?b");
????????????????????????}
????????????????????}?catch?(InterruptedException?e)?{
????????????????????????e.printStackTrace();
????????????????????}
????????????????}
????????????}
????????});
????????Thread?threadB?=?new?Thread(new?Runnable()?{????????????@Override
????????????public?void?run()?{????????????????synchronized?(resource_b)?{
????????????????????System.out.println("get?resource?b");????????????????????synchronized?(resource_a)?{
????????????????????????System.out.println("get?resource?a");
????????????????????}
????????????????}
????????????}
????????});
????????threadA.start();
????????threadB.start();

????}
}

在上面的這個(gè)demo中,開(kāi)啟了兩個(gè)線(xiàn)程threadA, threadB,其中threadA占用了resource_a, 并等待被threadB釋放的resource _b。threadB占用了resource _b正在等待被threadA釋放的resource _a。因此threadA,threadB出現(xiàn)線(xiàn)程安全的問(wèn)題,形成死鎖。同樣可以通過(guò)jps,jstack證明這種推論:

"Thread-1":
??waiting?to?lock?monitor?0x000000000b695360?(object?0x00000007d5ff53a8,?a?java.lang.String),
??which?is?held?by?"Thread-0""Thread-0":
??waiting?to?lock?monitor?0x000000000b697c10?(object?0x00000007d5ff53d8,?a?java.lang.String),
??which?is?held?by?"Thread-1"Java?stack?information?for?the?threads?listed?above:
==================================================="Thread-1":????????at?learn.DeadLockDemo$2.run(DeadLockDemo.java:34)
????????-?waiting?to?lock?<0x00000007d5ff53a8(a?java.lang.String)
????????-?locked?<0x00000007d5ff53d8(a?java.lang.String)????????at?java.lang.Thread.run(Thread.java:722)"Thread-0":????????at?learn.DeadLockDemo$1.run(DeadLockDemo.java:20)
????????-?waiting?to?lock?<0x00000007d5ff53d8(a?java.lang.String)
????????-?locked?<0x00000007d5ff53a8(a?java.lang.String)????????at?java.lang.Thread.run(Thread.java:722)Found?1?deadlock.

如上所述,完全可以看出當(dāng)前死鎖的情況。

那么,通??梢杂萌缦路绞奖苊馑梨i的情況:

  1. 避免一個(gè)線(xiàn)程同時(shí)獲得多個(gè)鎖;

  2. 避免一個(gè)線(xiàn)程在鎖內(nèi)部占有多個(gè)資源,盡量保證每個(gè)鎖只占用一個(gè)資源;

  3. 嘗試使用定時(shí)鎖,使用lock.tryLock(timeOut),當(dāng)超時(shí)等待時(shí)當(dāng)前線(xiàn)程不會(huì)阻塞;

  4. 對(duì)于數(shù)據(jù)庫(kù)鎖,加鎖和解鎖必須在一個(gè)數(shù)據(jù)庫(kù)連接里,否則會(huì)出現(xiàn)解鎖失敗的情況

所以,如何正確的使用多線(xiàn)程編程技術(shù)有很大的學(xué)問(wèn),比如如何保證線(xiàn)程安全,如何正確理解由于JMM內(nèi)存模型在原子性,有序性,可見(jiàn)性帶來(lái)的問(wèn)題,比如數(shù)據(jù)臟讀,DCL等這些問(wèn)題(在后續(xù)篇幅會(huì)講述)。而在學(xué)習(xí)多線(xiàn)程編程技術(shù)的過(guò)程中也會(huì)讓你收獲頗豐。

3. 應(yīng)該了解的概念

3.1 同步VS異步

同步和異步通常用來(lái)形容一次方法調(diào)用。同步方法調(diào)用一開(kāi)始,調(diào)用者必須等待被調(diào)用的方法結(jié)束后,調(diào)用者后面的代碼才能執(zhí)行。而異步調(diào)用,指的是,調(diào)用者不用管被調(diào)用方法是否完成,都會(huì)繼續(xù)執(zhí)行后面的代碼,當(dāng)被調(diào)用的方法完成后會(huì)通知調(diào)用者。比如,在超時(shí)購(gòu)物,如果一件物品沒(méi)了,你得等倉(cāng)庫(kù)人員跟你調(diào)貨,直到倉(cāng)庫(kù)人員跟你把貨物送過(guò)來(lái),你才能繼續(xù)去收銀臺(tái)付款,這就類(lèi)似同步調(diào)用。而異步調(diào)用了,就像網(wǎng)購(gòu),你在網(wǎng)上付款下單后,什么事就不用管了,該干嘛就干嘛去了,當(dāng)貨物到達(dá)后你收到通知去取就好。

3.2 并發(fā)與并行

并發(fā)和并行是十分容易混淆的概念。并發(fā)指的是多個(gè)任務(wù)交替進(jìn)行,而并行則是指真正意義上的“同時(shí)進(jìn)行”。實(shí)際上,如果系統(tǒng)內(nèi)只有一個(gè)CPU,而使用多線(xiàn)程時(shí),那么真實(shí)系統(tǒng)環(huán)境下不能并行,只能通過(guò)切換時(shí)間片的方式交替進(jìn)行,而成為并發(fā)執(zhí)行任務(wù)。真正的并行也只能出現(xiàn)在擁有多個(gè)CPU的系統(tǒng)中。

3.3 阻塞和非阻塞

阻塞和非阻塞通常用來(lái)形容多線(xiàn)程間的相互影響,比如一個(gè)線(xiàn)程占有了臨界區(qū)資源,那么其他線(xiàn)程需要這個(gè)資源就必須進(jìn)行等待該資源的釋放,會(huì)導(dǎo)致等待的線(xiàn)程掛起,這種情況就是阻塞,而非阻塞就恰好相反,它強(qiáng)調(diào)沒(méi)有一個(gè)線(xiàn)程可以阻塞其他線(xiàn)程,所有的線(xiàn)程都會(huì)嘗試地往前運(yùn)行。

3.4 臨界區(qū)

臨界區(qū)用來(lái)表示一種公共資源或者說(shuō)是共享數(shù)據(jù),可以被多個(gè)線(xiàn)程使用。但是每個(gè)線(xiàn)程使用時(shí),一旦臨界區(qū)資源被一個(gè)線(xiàn)程占有,那么其他線(xiàn)程必須等待。

名稱(chēng)欄目:透徹講解:并發(fā)編程的優(yōu)缺點(diǎn)
文章鏈接:http://bm7419.com/article20/jdghjo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供移動(dòng)網(wǎng)站建設(shè)、用戶(hù)體驗(yàn)品牌網(wǎng)站建設(shè)、品牌網(wǎng)站設(shè)計(jì)、網(wǎng)站設(shè)計(jì)、App開(kāi)發(fā)

廣告

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

成都app開(kāi)發(fā)公司