當我們談Java并發(fā)的時候,你們在談什么?

前言:

成都創(chuàng)新互聯(lián)從2013年創(chuàng)立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目做網(wǎng)站、成都網(wǎng)站建設(shè)網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元富陽做網(wǎng)站,已為上家服務(wù),為富陽各地企業(yè)和個人服務(wù),聯(lián)系電話:18980820575

很多人在剛開始學(xué) Java 的時候,會覺得多線程是一塊難啃的骨頭,特別是對于非科班的同學(xué)。究其原因,我想主要是因為沒有將多線程建立起一種模型,不清楚多線程的問題到底是怎么產(chǎn)生的。在這里,我就和大家聊一下我對Java 多線程的一些想法。

Java 是基于 Java 虛擬機(JVM)實現(xiàn)的一套編程語言,我們寫的 Java 代碼是要在 JVM 中才能運行。所謂虛擬機,其實就是模擬了一個操作系統(tǒng)。一個常規(guī)操作系統(tǒng)所必備的功能,虛擬機一般也會有。我們計算機的操作系統(tǒng)能管理內(nèi)存資源,那么虛擬機當然也要能管理內(nèi)存資源了。在 JVM 里,從邏輯的角度來說,會把內(nèi)存劃分為兩部分:  線程棧  和  堆  。

嗯,我知道你們對這樣簡單粗暴的劃分方式有意見,JVM 里面的內(nèi)存劃分遠比上面說的復(fù)雜。

而我們今天的談?wù)撝簧婕暗?nbsp; 線程棧  (即虛擬機棧)和  堆  ,因此就簡單地認為 JVM 只劃分了這兩部分。

也就是說,JVM 里面的內(nèi)存模型,我們可以簡要地畫成下面的那樣:

當我們談 Java 并發(fā)的時候,你們在談什么?

每一個線程對應(yīng)一個線程棧,線程棧里面的資源是私有的,也就是說我們在線程棧里的變量(即所謂的局部變量)是不會被多個線程共享。

堆內(nèi)存是被所有線程所共享的,程序中創(chuàng)建的對象都會保留在堆內(nèi)存中。

好了,說完了 Java 的內(nèi)存模型,我們來看一看計算機的內(nèi)存模型。

我們寫代碼的時候,代碼和數(shù)據(jù)一般都是保存在硬盤中。當我們在 shell 中輸入完一個 javac命令,或者點擊 IDE 的編譯按鈕的時候,我們的代碼和數(shù)據(jù)會第一時間復(fù)制到內(nèi)存中。復(fù)制完成之后會通知我們的 CPU 處理器,然后 CPU 開始執(zhí)行命令,將內(nèi)存中的信息復(fù)制到 CPU 寄存器中,用來執(zhí)行相應(yīng)指令。在現(xiàn)代 CPU 中,CPU 寄存器運行速度非常快,而內(nèi)存運行速度相對來說就非常慢了,為了彌補兩者運行速度之間的巨大差異,在內(nèi)存和 CPU 寄存器之間會有高速緩存(一般有三級緩存),用來暫時存放從內(nèi)存中獲取的數(shù)據(jù)。整個結(jié)構(gòu)大體如下圖:

當我們談 Java 并發(fā)的時候,你們在談什么?

上面這幅圖就是計算機的簡單存儲模型,這里只畫了三層,第一層是 CPU 寄存器,第二層是 CPU 高速緩存,第三層是內(nèi)存。這里的箭頭可以理解為數(shù)據(jù)總線,表示數(shù)據(jù)流動的方向。

在真實計算機中,CPU 高速緩存一般有多級,其中一部分封裝在 CPU 核中,另一部分封裝在 CPU 處理器中(一個處理器可以有多個核),這里為了方便,默認都封裝在 CPU 處理器中的。

如果 CPU 想要讀取我們代碼中的數(shù)據(jù),CPU 會先在高速緩存中查找需要的數(shù)據(jù),如果找到了,那么就直接使用這數(shù)據(jù);如果在緩存中沒有找到需要的數(shù)據(jù),那么就會繼續(xù)往下找,在內(nèi)存中獲取數(shù)據(jù),并且在緩存中存放一份,再拿回 CPU 使用。

而 CPU 想要把處理后的數(shù)據(jù)寫回來的時候,就稍微麻煩一些了。如果 CPU 返回一個數(shù)據(jù),就把該數(shù)據(jù)一級一級地往下送的話,那么數(shù)據(jù)總線流量就會非常大。因此,什么時間、以什么樣的方式將返回的數(shù)據(jù)寫入下一級存儲器,以達到性能最優(yōu),是一個比較困難的問題。我們只知道, CPU 返回一個數(shù)據(jù)后,我們不會立即在內(nèi)存中看到這個數(shù)據(jù) 。

了解了計算機的內(nèi)存模型,這和 JVM 的內(nèi)存模型有什么關(guān)系呢?

我們已經(jīng)知道,計算機的內(nèi)存模型和 JVM 的內(nèi)存模型是不一樣的,計算機的內(nèi)存模型里面并不區(qū)分線程棧和堆。而 JVM 里的堆和線程棧信息,一開始也只在計算機的內(nèi)存中,只有當 CPU 運行指令需要堆或線程棧中的信息時,JVM 里面的一部分堆和線程棧的數(shù)據(jù)才會被加載到高速緩存和 CPU 寄存器中。因此,JVM 的線程棧和堆的信息可以用下面的圖來表示:

當我們談 Java 并發(fā)的時候,你們在談什么?

也就是說,JVM 里面的變量和對象,可能在計算機存儲結(jié)構(gòu)中的任何地方存在。這就會導(dǎo)致兩個問題:

當線程更新一個共享變量的值時,會發(fā)生內(nèi)存可見性問題(Memory Visibility)。

當多個線程對同一個變量進行更新操作時,會產(chǎn)生競態(tài)條件(Race Condition)。

這里其實還可以思考一個問題,即在 JVM 里面進行的線程操作,是如何分布到操作系統(tǒng)的線程的。換句話說,JVM 里面的線程是用戶態(tài)還是內(nèi)核態(tài)?

其實 JVM 虛擬機規(guī)范并未對此作出限制,不同的 JVM 可以有不同的實現(xiàn)。HotSpot 虛擬機默認使用的是內(nèi)核線程,也就是說 HotSpot 虛擬機不干涉線程的調(diào)度,全權(quán)交由操作系統(tǒng)來處理。當然,如果想將線程綁定到特定的 CPU 核執(zhí)行,也是可以的。HotSpot 虛擬機中實現(xiàn)了 static bool bind_to_processor(uint processor_id); 方法,用來將線程綁定到指定的 CPU 核運行。

在此我向大家推薦一個Java高級群 : 725633148   里面會分享一些資深架構(gòu)師錄制的視頻錄像:(有Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化、分布式架構(gòu)、面試資料)等這些成為架構(gòu)師必備的知識體系 進群馬上免費領(lǐng)取,目前受益良多!

內(nèi)存可見性

假設(shè)有一個共享對象,它最開始只是在內(nèi)存中,當一個線程爭取到了左 CPU 的時間片,在這段時間里將共享對象復(fù)制到左 CPU 的高速緩存中,然后左 CPU 對這個共享對象做了一些修改并返回這個共享對象。之前我們說過, CPU 返回一個數(shù)據(jù)后,我們不會立即在內(nèi)存中看到這個數(shù)據(jù),因此,在共享對象的值返回到內(nèi)存之前,如果右 CPU 也想使用這個共享對象,那么右 CPU 拿到的共享對象不是左 CPU 修改后的共享對象,也就是說右 CPU 得到的共享對象的值不是最新的!

下面通過一副圖來說明這個問題:

當我們談 Java 并發(fā)的時候,你們在談什么?

在上圖中,左邊的 CPU 會將內(nèi)存中的 obj 對象復(fù)制一份在 CPU 高速緩存中,然后 CPU 對其進行操作,修改了 obj 對象中 count 屬性的值,讓 obj.count 從 1 變成了 2。然而在 CPU 高速緩存把 obj 最新的值返回到內(nèi)存中之前,右邊的 CPU 執(zhí)行了相同的代碼,也從內(nèi)存中獲取了 obj 對象,但它不知道左邊的 CPU 對 obj 對象進行修改了,它  看不見  obj 對象最新的值,因此,右邊的 CPU 獲取的 obj.count 的值還是 1 。

競態(tài)條件

可見性問題說的是一個線程對共享變量修改了之后,其他線程不能立即看到該共享變量最新的值得問題。如果有多個線程對同一個變量進行讀取和修改,那么就可能發(fā)生競態(tài)條件。

當我們談 Java 并發(fā)的時候,你們在談什么?

如上圖,假設(shè)左邊的 CPU 從內(nèi)存中獲取了 obj 對象,并將其復(fù)制到 CPU 高速緩存中,這個時候,右邊的 CPU 也從內(nèi)存中獲取到了 obj 對象,也將其復(fù)制到了 CPU 高速緩存中。然后兩個 CPU 都對 obj.count 的值增加 1。從整體上來看,obj.count 的值增加了兩次,而當左右兩邊的 CPU 高速緩存將 obj 的值寫回到內(nèi)存中時,會發(fā)現(xiàn)實際上 obj.count 的值只增加了 1 次。

下面的流程圖可以詳細說明這種情況:

當我們談 Java 并發(fā)的時候,你們在談什么?

左 CPU 和右 CPU 同時爭奪 obj 對象的情況,就被成為“競態(tài)條件”。

在此我向大家推薦一個Java高級群 : 725633148   里面會分享一些資深架構(gòu)師錄制的視頻錄像:(有Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化、分布式架構(gòu)、面試資料)等這些成為架構(gòu)師必備的知識體系 進群馬上免費領(lǐng)取,目前受益良多!

分享文章:當我們談Java并發(fā)的時候,你們在談什么?
當前鏈接:http://bm7419.com/article24/gipdce.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供搜索引擎優(yōu)化、網(wǎng)站排名用戶體驗、電子商務(wù)網(wǎng)站制作、品牌網(wǎng)站制作

廣告

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

商城網(wǎng)站建設(shè)