您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“怎么理解java并發(fā)編程的三個核心問題”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“怎么理解java并發(fā)編程的三個核心問題”吧!
總的來說,并發(fā)編程可以總結(jié)為三個核心問題:分工、同步、互斥。
所謂分工指的是如何高效地拆解任務(wù)并分配給線程,而同步指的是線程之間如何協(xié)作,互斥則是保證同一時刻只允許一個線程訪問共享資源。java SDK并發(fā)包很大部分內(nèi)容都是按照這三個維度組織的,例如Fork/Join框架就是一種分工模式,CountDownLatch就是一種典型的同步方式,而可重入鎖則是一種互斥手段。
1.分工
所謂分工,類似于現(xiàn)實中一個組織完成一個項目,項目經(jīng)理要拆分任務(wù),安排合適的成員去完成。
在并發(fā)編程領(lǐng)域,你就是項目經(jīng)理,線程就是項目組成員。任務(wù)分解和分工對于項目成敗非常關(guān)鍵,不過在并發(fā)領(lǐng)域里,分工更重要,它直接決定了并發(fā)程序的性能。在現(xiàn)實世界里,分工是很復(fù)雜的,著名數(shù)學(xué)家華羅庚曾用“燒水泡茶”的例子通俗地講解了統(tǒng)籌方法(一種安排工作進程的數(shù)學(xué)方法),“燒水泡茶”這么簡單的事情都這么多說道,更何況是并發(fā)編程里的工程問題呢。
既然分工很重要又很復(fù)雜,那一定有前輩努力嘗試解決過,并且也一定有成果。的確,在并發(fā)編程領(lǐng)域這方面的成果還是很豐碩的。Java SDK并發(fā)包里的Executor、Fork/Join、Future本質(zhì)上就是一種分工方法。除此之外,并發(fā)編程領(lǐng)域還總結(jié)了一些設(shè)計模式,基本上都是和分工方法相關(guān)的,例如生產(chǎn)者-消費者、Thread-Per-Message、Worker Thread模式等都是用來指導(dǎo)你如何分工的。
了解這部分內(nèi)容,最佳的方式就是和現(xiàn)實世界做對比。例如生產(chǎn)者-消費者模式,可以類比一下餐館里的大廚和服務(wù)員,大廚就是生產(chǎn)者,負責(zé)做菜,做完放到出菜口,而服務(wù)員就是消費者,把做好的菜給你端過來。不過,我們經(jīng)常會發(fā)現(xiàn),出菜口有時候一下子出了好幾個菜,服務(wù)員是可以吧這一批菜同時端給你的。其實這就是生產(chǎn)者-消費者模式的一個優(yōu)點,生產(chǎn)者一個一個地生產(chǎn)數(shù)據(jù),而消費者可以批處理,這樣就提高了性能。
2.同步
分好工之后,就是具體執(zhí)行了。在項目執(zhí)行過程中,任務(wù)之間是有依賴的,一個任務(wù)結(jié)束后,依賴它的后續(xù)任務(wù)任務(wù)就可以開工了,后續(xù)工作怎么知道可以開工了呢?這個就是靠溝通協(xié)作了,這是一項很重要的工作。
在并發(fā)編程領(lǐng)域里的同步,主要指的就是線程間的協(xié)作,本質(zhì)上和現(xiàn)實生活中的協(xié)作沒區(qū)別,不過是一個線程執(zhí)行完了一個任務(wù),如何通知執(zhí)行后續(xù)任務(wù)的線程開工而已。協(xié)作一般是和分工相關(guān)的。Java SDK并發(fā)包里的Executor、Fork/Join、Future本質(zhì)上都是分工方法,但同時也能解決線程協(xié)作的問題。例如,用Future可以發(fā)起一個異步調(diào)用,當主線程通過get()方法取結(jié)果時,主線程就會等待,當異步執(zhí)行的結(jié)果返回時,get()方法就自動返回了。主線程和異步線程之間的協(xié)作,F(xiàn)uture工具類已經(jīng)幫我們解決了。除此之外,Java SDK里提供的CountDownLatch、CyclicBarrier、Phaser、Exchanger也都是用來解決線程協(xié)作的問題。
不過還有很多場景,是需要你自己來處理線程之間的協(xié)作的。
工作中遇到的線程協(xié)作的問題,基本上都可以描述為這樣的一個問題:當某個條件不滿足時,線程需要等待,當某個條件滿足時,線程需要被喚醒執(zhí)行。例如,在生產(chǎn)者-消費者模型里,也有類似的描述,“當隊列滿時,生產(chǎn)者線程等待,當隊列不滿時,生產(chǎn)者線程需要被喚醒執(zhí)行;當隊列空時,消費者線程等待,當隊列不為空時,消費者線程需要被喚醒執(zhí)行?!?/p>
在Java并發(fā)編程領(lǐng)域,解決協(xié)作問題的核心技術(shù)是管程,上面提到的所有線程協(xié)作技術(shù)底層都是利用管程解決的。管程是一種解決并發(fā)問題的通用模型,除了能解決線程協(xié)作問題,還能解決下面我們將要介紹的互斥問題??梢赃@么說,管程是解決并發(fā)問題的萬能鑰匙。
所以說,這部分內(nèi)容的學(xué)習(xí),關(guān)鍵是理解管程模型,學(xué)好它就可以解決所有問題。其次,了解Java SDK并發(fā)包提供的幾個線程協(xié)作的工具類的應(yīng)用場景,用好它們可以妥妥地提高你的工作效率。
3.互斥
分工、同步主要強調(diào)的是性能,但并發(fā)程序里還有一部分是關(guān)于正確性的,用專業(yè)術(shù)語叫“線程安全”。并發(fā)程序里,當多個線程同時訪問一個共享變量時,結(jié)果時不確定的。不確定,則意味著可能正確,也可能錯誤,事先是不知道的。而導(dǎo)致不確定的主要源頭是可見性的問題、有序性問題和原子性問題,為了解決這三個問題,Java語言引入了內(nèi)存模型,內(nèi)存模型提供了一系列的規(guī)則,利用這些規(guī)則,我們可以避免可見性問題、有序性問題,但是還不足以完全解決線程安全問題。解決線程安全問題的核心方案還是互斥。
所謂互斥,指的是同一時刻,只允許一個線程訪問共享變量。
實現(xiàn)互斥的核心技術(shù)就是鎖,Java語言里synchronized、SDK里的各種Lock都能解決互斥問題。雖說鎖解決了安全性問題,但同時也帶來了性能問題,那如何保證安全性的同時又盡量提高性能呢?可以分場景優(yōu)化,Java SDK里提供的ReadWriteLock、StampedLock就可以優(yōu)化讀多寫少場景下鎖的性能。還可以使用無鎖的數(shù)據(jù)結(jié)構(gòu),例如Java SDK里提供的原子類都是基于無鎖技術(shù)實現(xiàn)的。
除此之外,還有一些其他的方案,原理是不共享變量或者變量只允許讀。這方面,Java 提供了Thread Local和final關(guān)鍵字,還有一種copy-on-write的模式。
使用鎖除了要注意性能問題外,還需要注意死鎖問題。
這部分內(nèi)容比較復(fù)雜,往往還是跨領(lǐng)域的,例如要理解可見性,就需要了解一些CPU和緩存的知識;要理解原子性,就需要理解一些操作系統(tǒng)的知識;很多無鎖算法的實現(xiàn)往往也需要理解CPU緩存。這部分內(nèi)容,需要博覽群書,在大腦里建立起CPU、內(nèi)存、I/O執(zhí)行的模擬器。這樣遇到問題就能得心應(yīng)手了。
到此,相信大家對“怎么理解java并發(fā)編程的三個核心問題”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。