国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
并發(fā)編程之痛

一個(gè)程序員開始學(xué)習(xí)編程,熟悉了某個(gè)編程語(yǔ)言的語(yǔ)法和庫(kù),就可以寫程序了,比如操作文件、處理數(shù)據(jù)庫(kù)中的數(shù)據(jù)等等,把這些程序放到框架中,還能給互聯(lián)網(wǎng)上的用戶提供服務(wù)。那這里面會(huì)不會(huì)涉及到并發(fā)編程呢?如果你沒有在代碼中顯式使用線程 API,那可能你的程序就是串行的。但是它為什么可以提供并發(fā)服務(wù)呢,也就是說(shuō),很多人可以同時(shí)訪問你的程序功能?這是因?yàn)槟闶怯玫目蚣?—— Nginx、OpenResty、Tomcat 或數(shù)據(jù)庫(kù)連接池等 —— 提供了并發(fā)服務(wù)。比如 Nginx 中的 worker 調(diào)度服務(wù)。

你當(dāng)然可以使用現(xiàn)有的線程并發(fā)框架,但是,總有一天你會(huì)自己去編寫多線程并發(fā)程序,這是任何一個(gè)程序員無(wú)法避開的挑戰(zhàn),它就像你苦練十年準(zhǔn)備跨馬提刀闖江湖時(shí)必須要打敗的一個(gè)對(duì)手。事實(shí)上,并發(fā)編程并不像在花園陪妹子散步那么輕松。

在串行程序中,代碼的執(zhí)行路徑是可預(yù)測(cè)的,遇到問題。你總可以順藤摸瓜找到邏輯錯(cuò)誤或某種 bug,比如內(nèi)存泄露、緩存區(qū)溢出、文件和網(wǎng)絡(luò) IO 錯(cuò)誤、數(shù)組越界等等。在并發(fā)算法和多線程編程中,你需要同時(shí)考慮有多個(gè)執(zhí)行流程的情況,以及如何對(duì)這些流程進(jìn)行協(xié)調(diào)以完成指定的計(jì)算任務(wù)。并發(fā)編程中的很多 bug 是由于線程并發(fā)執(zhí)行中的不確定性和異步性導(dǎo)致的。在不同的機(jī)器環(huán)境,線程會(huì)按照某種次序執(zhí)行而導(dǎo)致錯(cuò)誤無(wú)法被發(fā)現(xiàn),等上線了才知道,已經(jīng)是生產(chǎn)事故了。

如何在多核和云平臺(tái)上寫出健壯、安全、高性能的并發(fā)程序呢?

其實(shí)很簡(jiǎn)單,只要你能從兩個(gè)方面突破一下就可以了。一個(gè)是“跳出來(lái),看全景”,另一個(gè)是“鉆進(jìn)去,看本質(zhì)”。

跳出來(lái),看全景

我們先說(shuō)“跳出來(lái)”。你應(yīng)該也知道,學(xué)習(xí)最忌諱的就是“盲人摸象”,只看到局部,而沒有看到全局。所以,你需要從一個(gè)個(gè)單一的知識(shí)和技術(shù)中“跳出來(lái)”,高屋建瓴地看并發(fā)編程。當(dāng)然,這首要之事就是你建立起一張全景圖。

不過(guò),并發(fā)編程相關(guān)的知識(shí)和技術(shù)還真是錯(cuò)綜復(fù)雜,時(shí)至今日也還沒有一張普遍認(rèn)可的全景圖,也許這正是很多人在并發(fā)編程方面難以突破的原因吧。好在經(jīng)過(guò)多年摸爬滾打,我自己已經(jīng)“勾勒”出了一張全景圖,不一定科學(xué),但是在某種程度上我想它還是可以指導(dǎo)你學(xué)好并發(fā)編程的。

在我看來(lái),并發(fā)編程領(lǐng)域可以抽象成三個(gè)核心問題:分工、同步和互斥

1、分工

所謂分工,類似于現(xiàn)實(shí)中一個(gè)組織完成一個(gè)項(xiàng)目,項(xiàng)目經(jīng)理要拆分任務(wù),安排合適的成員去完成。

在并發(fā)編程領(lǐng)域,你就是項(xiàng)目經(jīng)理,線程就是項(xiàng)目組成員。任務(wù)分解和分工對(duì)于項(xiàng)目成敗非常關(guān)鍵,不過(guò)在并發(fā)領(lǐng)域里,分工更重要,它直接決定了并發(fā)程序的性能。在現(xiàn)實(shí)世界里,分工是很復(fù)雜的,著名數(shù)學(xué)家華羅庚曾用“燒水泡茶”的例子通俗地講解了統(tǒng)籌方法(一種安排工作進(jìn)程的數(shù)學(xué)方法),“燒水泡茶”這么簡(jiǎn)單的事情都這么多說(shuō)道,更何況是并發(fā)編程里的工程問題呢。

既然分工很重要又很復(fù)雜,那一定有前輩努力嘗試解決過(guò),并且也一定有成果。的確,在并發(fā)編程領(lǐng)域這方面的成果還是很豐碩的。Java SDK 并發(fā)包里的Executor、Fork/Join、Future 本質(zhì)上都是一種分工方法。除此之外,并發(fā)編程領(lǐng)域還總結(jié)了一些設(shè)計(jì)模式,基本上都是和分工方法相關(guān)的,例如生產(chǎn)者-消費(fèi)者、Thread-Per-Message、Worker Thread 模式等都是用來(lái)指導(dǎo)你如何分工的。

學(xué)習(xí)這部分內(nèi)容,最佳的方式就是和現(xiàn)實(shí)世界做對(duì)比。例如生產(chǎn)者-消費(fèi)者模式,可以類比一下餐館里的大廚和服務(wù)員,大廚就是生產(chǎn)者,負(fù)責(zé)做菜,做完放到出菜口,而服務(wù)員就是消費(fèi)者,把做好的菜給你端過(guò)來(lái)。不過(guò),我們經(jīng)常會(huì)發(fā)現(xiàn),出菜口有時(shí)候一下子出了好幾個(gè)菜,服務(wù)員是可以把這一批菜同時(shí)端給你的。其實(shí)這就是生產(chǎn)者-消費(fèi)者模式的一個(gè)優(yōu)點(diǎn),生產(chǎn)者一個(gè)一個(gè)地生產(chǎn)數(shù)據(jù),而消費(fèi)者可以批處理,這樣就提高了性能。

2、同步

分好工之后,就是具體執(zhí)行了。在項(xiàng)目執(zhí)行過(guò)程中,任務(wù)之間是有依賴的,一個(gè)任務(wù)結(jié)束后,依賴它的后續(xù)任務(wù)就可以開工了,后續(xù)工作怎么知道可以開工了呢?這個(gè)就是靠溝通協(xié)作了,這是一項(xiàng)很重要的工作。

在并發(fā)編程領(lǐng)域里的同步,主要指的就是線程間的協(xié)作,本質(zhì)上和現(xiàn)實(shí)生活中的協(xié)作沒區(qū)別,不過(guò)是一個(gè)線程執(zhí)行完了一個(gè)任務(wù),如何通知執(zhí)行后續(xù)任務(wù)的線程開工而已。

協(xié)作一般是和分工相關(guān)的。Java SDK 并發(fā)包里的 Executor、Fork/Join、Future 本質(zhì)上都是分工方法,但同時(shí)也能解決線程協(xié)作的問題。例如,用 Future 可以發(fā)起一個(gè)異步調(diào)用,當(dāng)主線程通過(guò) get() 方法取結(jié)果時(shí),主線程就會(huì)等待,當(dāng)異步執(zhí)行的結(jié)果返回時(shí),get() 方法就自動(dòng)返回了。主線程和異步線程之間的協(xié)作,F(xiàn)uture 工具類已經(jīng)幫我們解決了。除此之外,Java SDK 里提供的 CountDownLatch、CyclicBarrier、Phaser、Exchanger 也都是用來(lái)解決線程協(xié)作問題的。

不過(guò)還有很多場(chǎng)景,是需要你自己來(lái)處理線程之間的協(xié)作的。

工作中遇到的線程協(xié)作問題,基本上都可以描述為這樣的一個(gè)問題:當(dāng)某個(gè)條件不滿足時(shí),線程需要等待,當(dāng)某個(gè)條件滿足時(shí),線程需要被喚醒執(zhí)行。例如,在生產(chǎn)者-消費(fèi)者模型里,也有類似的描述,“當(dāng)隊(duì)列滿時(shí),生產(chǎn)者線程等待,當(dāng)隊(duì)列不滿時(shí),生產(chǎn)者線程需要被喚醒執(zhí)行;當(dāng)隊(duì)列空時(shí),消費(fèi)者線程等待,當(dāng)隊(duì)列不空時(shí),消費(fèi)者線程需要被喚醒執(zhí)行?!?/p>

在Java并發(fā)編程領(lǐng)域,解決協(xié)作問題的核心技術(shù)是管程,上面提到的所有線程協(xié)作技術(shù)底層都是利用管程解決的。管程是一種解決并發(fā)問題的通用模型,除了能解決線程協(xié)作問題,還能解決下面我們將要介紹的互斥問題。可以這么說(shuō),管程是解決并發(fā)問題的萬(wàn)能鑰匙。

所以說(shuō),這部分內(nèi)容的學(xué)習(xí),關(guān)鍵是理解管程模型,學(xué)好它就可以解決所有問題。其次是了解Java SDK并發(fā)包提供的幾個(gè)線程協(xié)作的工具類的應(yīng)用場(chǎng)景,用好它們可以妥妥地提高你的工作效率。

3、互斥

分工、同步主要強(qiáng)調(diào)的是性能,但并發(fā)程序里還有一部分是關(guān)于正確性的,用專業(yè)術(shù)語(yǔ)叫“線程安全”。并發(fā)程序里,當(dāng)多個(gè)線程同時(shí)訪問同一個(gè)共享變量的時(shí)候,結(jié)果是不確定的。不確定,則意味著可能正確,也可能錯(cuò)誤,事先是不知道的。而導(dǎo)致不確定的主要源頭是可見性問題、有序性問題和原子性問題,為了解決這三個(gè)問題,Java 語(yǔ)言引入了內(nèi)存模型,內(nèi)存模型提供了一些列的規(guī)則,利用這些規(guī)則,我們可以避免可見性問題、有序性問題,但是還不足以完全解決線程安全問題。解決線程安全問題的核心方案還是互斥。

所謂互斥,指的是同一時(shí)刻,只允許一個(gè)線程訪問共享變量。

實(shí)現(xiàn)互斥的核心技術(shù)就是鎖,Java語(yǔ)言里 synchronized、SDK里的各種 Lock 都能解決互斥問題。雖說(shuō)鎖解決了安全性問題,但同時(shí)也帶來(lái)了性能問題,那如何保證安全性的同時(shí)又盡量提高性能呢?可以分場(chǎng)景優(yōu)化,Java SDK 里提供的ReadWriteLock、StampedLock 就可以優(yōu)化讀多寫少場(chǎng)景下鎖的性能。還可以使用無(wú)鎖的數(shù)據(jù)結(jié)構(gòu),例如 Java SDK 里提供的原子類都是基于無(wú)鎖技術(shù)實(shí)現(xiàn)的。

除此之外,還有一些其他的方案,原理是不共享變量或者變量只允許讀。這方面,Java 提供了 Thread Local 和final 關(guān)鍵字,還有一種 Copy-on-write 的模式。

使用鎖除了要注意性能問題外,還需要注意死鎖問題。

這部分內(nèi)容比較復(fù)雜,往往還是跨領(lǐng)域的,例如要理解可見性,就需要了解一些 CPU 和緩存的知識(shí);要理解原子性,就需要理解一些操作系統(tǒng)的知識(shí);很多無(wú)鎖算法的實(shí)現(xiàn)往往也需要理解 CPU 緩存。這部分內(nèi)容的學(xué)習(xí),需要博覽群書,在大腦里建立起 CPU、內(nèi)存、I/O 執(zhí)行的模擬器。這樣遇到問題就能得心應(yīng)手了。

跳出來(lái),看全景,可以讓你的知識(shí)成體系,所學(xué)知識(shí)也融匯貫通起來(lái),由點(diǎn)成線,由線及面,畫出自己的知識(shí)全景圖。

并發(fā)編程全景圖之思維導(dǎo)圖

鉆進(jìn)去,看本質(zhì)

但是光跳出來(lái)還不夠,還需要下一步,就是在某個(gè)問題上鉆進(jìn)去,深入理解,找到本質(zhì)。

就拿我個(gè)人來(lái)說(shuō),我已經(jīng)煩透了去講述或被講述一堆概念和結(jié)論,而不分析這些概念和結(jié)論是怎么來(lái)的,以及它們是用來(lái)解決什么問題的。在大學(xué)里,這樣的教材很流行,直接導(dǎo)致了蕓蕓學(xué)子成績(jī)很高,但解決問題的能力很差。其實(shí),知其然知其所以然,才算真的學(xué)明白了。

我屬于理論派,我認(rèn)為工程上的解決方案,一定要有理論做基礎(chǔ)。所以在學(xué)習(xí)并發(fā)編程的過(guò)程中,我都會(huì)探索它背后的理論是什么。比如,當(dāng)看到Java SDK里面的條件變量Condition的時(shí)候,我會(huì)下意識(shí)地問,“它是從哪兒來(lái)的?是Java的特有概念,還是一個(gè)通用的編程概念?”當(dāng)我知道他來(lái)自管程的時(shí)候,我又會(huì)問,“管程被提出的背景和解決的問題是什么?”這樣一路探索下來(lái),我發(fā)現(xiàn)Java語(yǔ)言里的并發(fā)技術(shù)基本都是有理論基礎(chǔ)的,并且這些理論在其他編程語(yǔ)言里也有類似的實(shí)現(xiàn)。所以我認(rèn)為,技術(shù)的本質(zhì)是背后的理論模型。

總結(jié)

本文部分內(nèi)容來(lái)自極客時(shí)間新上線的專欄《Java 并發(fā)編程實(shí)戰(zhàn)》,作者王寶令老師是京東資深架構(gòu)師,寫過(guò)十五年程序,先后在用友、惠普等公司工作過(guò),主導(dǎo)研發(fā)了支持高并發(fā)處理能力的 API 網(wǎng)關(guān)、高性能數(shù)據(jù)庫(kù)連接池和海量數(shù)據(jù)歸檔平臺(tái)等。

他的專欄將從理論開始,以實(shí)戰(zhàn)和工具為主,為大家講述并發(fā)編程的故事。你將獲得:

1、全面了解并發(fā)編程核心原理
2、深入掌握 12 個(gè) Java 并發(fā)工具類
3、搞懂 9 種并發(fā)編程模式
4、四大經(jīng)典并發(fā)編程實(shí)戰(zhàn)案例

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
disruptor框架為什么這么強(qiáng)大
把Java編程語(yǔ)言精通到底有多難?
主題:JAVA多線程,真的提高了效率嗎?
Java初學(xué)者常見問題!拿走不謝!
Java 多線程(一) 基礎(chǔ)知識(shí)與概念
JAVA內(nèi)存模型和Happens-Before規(guī)則
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服