聊聊jvm之并發(fā)

衡量一個(gè)服務(wù)性能的高低好壞,每秒事務(wù)處理數(shù)(Transactions Per Second惠呼,TPS與QPS類似)是最重要的指標(biāo)之一导俘。

1、硬件效率與緩存一致性

絕大多數(shù)任務(wù)不可能只靠處理器計(jì)算剔蹋,處理器至少需要與內(nèi)存交互旅薄,如讀取運(yùn)算數(shù)據(jù)、存儲運(yùn)算結(jié)果滩租,這些I/O操作很難消除赋秀,由于計(jì)算機(jī)的存儲設(shè)備與處理器的運(yùn)算速度存在幾個(gè)數(shù)量級的差距,故計(jì)算機(jī)系統(tǒng)加了一層讀寫速度盡可能接近處理器運(yùn)算速度的高速緩存來作為內(nèi)存與處理器之間的緩沖律想×粤基于高速緩存的存儲交互解決了處理器與內(nèi)存的速度矛盾,同時(shí)引入了一個(gè)新的問題:緩存一致性(Cache Coherence)技即,每個(gè)處理器都有自己的緩沖著洼,同時(shí)又共享同一主內(nèi)存,當(dāng)多個(gè)處理器運(yùn)算任務(wù)涉及到同一塊內(nèi)存區(qū)域而叼,可能導(dǎo)致數(shù)據(jù)不一致情況身笤。此時(shí)需要各個(gè)處理器訪問緩存時(shí)遵循一些協(xié)議,這類協(xié)議有:MSI葵陵、MESI液荸、MOSI、Synapse脱篙、Firefly娇钱、Dragon Protocol。除增加高速緩存外绊困,處理器可能會對輸入的代碼進(jìn)行亂序執(zhí)行優(yōu)化文搂,在計(jì)算之后將亂序執(zhí)行結(jié)果重組,保證與順序執(zhí)行結(jié)果一致秤朗。

2煤蹭、Java內(nèi)存模型(Java Memory Modal,JMM)

Java內(nèi)存模型的主要目標(biāo)是定義程序中各個(gè)變量的訪問規(guī)則取视,即在虛擬機(jī)中將變量存儲到內(nèi)存和從內(nèi)存中取出變量這樣的底層細(xì)節(jié)硝皂。JMM規(guī)定了所有變量都存儲在主內(nèi)存中,每條線程有自己的工作內(nèi)存贫途,工作內(nèi)存保存了主內(nèi)存變量的副本拷貝吧彪,變量的讀取復(fù)制操作必須在工作內(nèi)存中進(jìn)行,線程間變量的傳遞需要通過主內(nèi)存來完成丢早。

變量的拷貝和同步,JMM定義了8種操作來完成:

lock(鎖定):作用于主內(nèi)存變量,標(biāo)識為一條線程獨(dú)占

unlock(解鎖):作用于主內(nèi)存變量怨酝,釋放鎖定狀態(tài)

read(讀取):作用于主內(nèi)存變量傀缩,將變量的值從主內(nèi)存?zhèn)鬏數(shù)焦ぷ鲀?nèi)存,以便隨后load動作作用

load(載入):作用于工作內(nèi)存农猬,將read操作的變量放入工作內(nèi)存副本當(dāng)中

use(使用):作用于工作內(nèi)存赡艰,將變量的值傳遞給執(zhí)行引擎,虛擬機(jī)遇到使用變量的值時(shí)都會有這個(gè)操作

assign(賦值):作用于工作內(nèi)存斤葱,把從執(zhí)行引擎接收到的值賦給工作內(nèi)存變量

store(存儲):作用于工作內(nèi)存慷垮,將工作中內(nèi)存變量傳遞給主內(nèi)存,以便write操作

write(寫入):作用于主內(nèi)存揍堕,把store操作從工作內(nèi)存的變量值放入主內(nèi)存變量中

由此可見料身,read\load成對出現(xiàn)、store/write成對出現(xiàn)衩茸,JMM還規(guī)定了執(zhí)行上述操作需要滿足的規(guī)則:

①芹血、不允許read和load,store和write單獨(dú)出現(xiàn)

②楞慈、不允許線程丟棄最近的assign操作幔烛,規(guī)定變量改變之后必須同步到主內(nèi)存

③、不允許線程無緣由地從工作線程同步到主內(nèi)存(需要執(zhí)行assign之后)

④囊蓝、use饿悬、store之前必須要執(zhí)行assig、load聚霜,新變量只能在主內(nèi)存中產(chǎn)生

⑤狡恬、一個(gè)變量同一時(shí)刻只能由一個(gè)線程進(jìn)行l(wèi)ock,多次lock需要對應(yīng)此時(shí)unlock俯萎,變量才會被解鎖

⑥傲宜、對一個(gè)變量執(zhí)行了lock操作,會清空工作內(nèi)存此變量的值夫啊。在執(zhí)行引擎使用前需要重新load或assign

⑦函卒、執(zhí)行unlock之前,必須將此變量同步會主內(nèi)存中

3撇眯、volatile變量特殊規(guī)則:

①保存此變量對所有線程可見性报嵌。但在并發(fā)下一樣不安全。volatile變量在各個(gè)線程的工作內(nèi)存中存在不一致性問題熊榛,但當(dāng)執(zhí)行引擎每次使用锚国,都會進(jìn)行刷新,也就可認(rèn)為不存在一致性問題玄坦。由于java的運(yùn)算并非原子操作血筑,volatile變量在并發(fā)情況下并不是安全的(A線程使用前已經(jīng)刷新绘沉,此時(shí)B線程將新值賦給工作內(nèi)存,此時(shí)A計(jì)算的數(shù)據(jù)就不是安全的)豺总。

②禁止指令重排序车伞,首先指令重排序是指CPU采用了允許將多條指令不按程序規(guī)定的順序分開發(fā)送給各相應(yīng)的電路單元處理,但并不是指令任意排序喻喳,CPU需要正確處理指令依賴情況以保障程序得到正確的執(zhí)行結(jié)果另玖。禁止指令重排序通過設(shè)置內(nèi)存屏障來實(shí)現(xiàn),指令重排序不能把后面的指令重排序到內(nèi)存屏障之前的位置表伦。

volatile變量的讀性能與普通變量幾乎沒什么差別谦去,但寫操作會慢一些,需要在本地代碼插入許多內(nèi)存屏障指令來保證處理器不發(fā)生亂序執(zhí)行蹦哼。即便如此鳄哭,大多數(shù)volatile的總開銷要比鎖低。

4翔怎、先行發(fā)生原則(happens-before)窃诉,判斷數(shù)據(jù)是否存在競爭、線程是否安全的主要依據(jù)赤套。如果說A操作先行發(fā)生于B操作飘痛,那么在B操作發(fā)生之前,A操作的影響能被B操作觀察到容握,“影響”包括修改內(nèi)存中共享變量的值宣脉、發(fā)送了消息、調(diào)用了方法等剔氏。

①塑猖、程序次序規(guī)則,一個(gè)線程內(nèi)按程序代碼順序執(zhí)行谈跛,前面的代碼先于后面的代碼羊苟。

②、管程鎖定規(guī)則感憾,一個(gè)unlock操作先于后面的lock操作

③蜡励、volatile規(guī)則,volatile前面的寫操作先于后面的讀操作

④阻桅、線程啟動規(guī)則凉倚,start()方法先于此線程的每一個(gè)動作

⑤、線程終止規(guī)則嫂沉,線程中所有操作都先于對此線程的終止檢測稽寒,Thread.join(),Thread.isAlive()檢測線程是否終止

⑥趟章、線程中斷規(guī)則杏糙,對線程interrup()方法調(diào)用先行被中斷線程檢測到中斷事件的發(fā)生慎王,通過Thread.interrupted檢測是否有中斷

⑦、對象終結(jié)規(guī)則搔啊,一個(gè)對象的初始化完成先行于finalize()方法

⑧柬祠、傳遞性北戏,A先行于B负芋,B先行于C,則A先行于C嗜愈。

5旧蛾、線程

線程是比進(jìn)程更輕量級的調(diào)度執(zhí)行單位,線程的引入蠕嫁,可以把一個(gè)進(jìn)程的資源分配和執(zhí)行調(diào)度分開锨天,各個(gè)線程既可以共享進(jìn)程資源,又可以獨(dú)立調(diào)度剃毒。

實(shí)現(xiàn)進(jìn)程的方式:內(nèi)核線程實(shí)現(xiàn)病袄、用戶線程實(shí)現(xiàn)和用戶線程+輕量級進(jìn)程混合實(shí)現(xiàn)。

①赘阀、內(nèi)核線程實(shí)現(xiàn)益缠,這種線程由內(nèi)核來完成線程切換,這種線程由叫輕量級進(jìn)程基公,每個(gè)輕量級進(jìn)程都由一個(gè)內(nèi)核線程支持幅慌,比例1:1。每個(gè)輕量級進(jìn)程都可作為一個(gè)獨(dú)立的調(diào)度單元轰豆,即使阻塞了胰伍,也不會影響進(jìn)程繼續(xù)工作∷嵝荩基于內(nèi)核實(shí)現(xiàn)骂租,故線程的創(chuàng)建、析構(gòu)斑司、同步渗饮,都需要進(jìn)行系統(tǒng)調(diào)用,需要在用戶態(tài)和內(nèi)核態(tài)之間頻繁轉(zhuǎn)換陡厘。

②抽米、用戶線程實(shí)現(xiàn),廣義上不屬于內(nèi)核線程糙置,就是用戶線程云茸,輕量級進(jìn)程也屬于用戶線程。狹義上谤饭,完全建立在用戶空間的線程才是用戶線程标捺,系統(tǒng)內(nèi)核不感知線程實(shí)現(xiàn)懊纳。線程的建立、同步亡容、銷毀和調(diào)度完全在用戶態(tài)中完成嗤疯。因而這類線程快速且低消耗,可支持更大規(guī)模的線程數(shù)量闺兢,部分高性能數(shù)據(jù)庫中多線程即用戶線程實(shí)現(xiàn)茂缚,進(jìn)程與用戶線程比例為1:N。但因操作系統(tǒng)只把資源分配到進(jìn)程屋谭,而線程的創(chuàng)建脚囊、切換、調(diào)度都需要考慮桐磁,卻類似阻塞悔耘、多處理器中線程映射到其他處理器等問題幾乎無法處理。

③我擂、用戶線程+輕量級進(jìn)程衬以,用戶進(jìn)程任然建立在用戶空間,操作系統(tǒng)提供的輕量級進(jìn)程作為用戶線程和內(nèi)核線程溝通的橋梁校摩,大大降低了進(jìn)程被完全阻塞的風(fēng)險(xiǎn)看峻。用戶線程與輕量級進(jìn)程比例為N:M。

Sun JDK中秧耗,Windows版本與Linux版本都是使用1:1線程模型實(shí)現(xiàn)备籽。

6、線程調(diào)度指系統(tǒng)為線程分配處理器使用權(quán)的過程分井,主要有協(xié)同式線程調(diào)度和搶占式線程調(diào)度车猬。

①、協(xié)同式線程調(diào)度尺锚,線程的執(zhí)行時(shí)間由線程本身調(diào)度珠闰,工作執(zhí)行完之后,主動通知系統(tǒng)切換到另一個(gè)線程上瘫辩。導(dǎo)致線程執(zhí)行時(shí)間不可控伏嗜,一直不通知系統(tǒng)切換,程序就一直阻塞伐厌。

②承绸、搶占式線程調(diào)度,每個(gè)線程由系統(tǒng)分配執(zhí)行時(shí)間挣轨,線程切換不由程序本身決定(Thread.yield()只能讓出cpu時(shí)間军熏,無法獲取執(zhí)行時(shí)間),java線程就使用搶占式調(diào)度卷扮,同時(shí)可設(shè)置優(yōu)先級(10個(gè)級別)建議系統(tǒng)給某些線程多分配一點(diǎn)執(zhí)行時(shí)間荡澎。

7均践、線程狀態(tài):

①、新建(New):創(chuàng)建后尚未啟動摩幔。

②彤委、運(yùn)行(Runable):包括操作系統(tǒng)線程狀態(tài)中的Runing和Ready,即可能正在運(yùn)行或衡,也可能正在等待分配CPU時(shí)間焦影。

③、無限等待(Waiting):這種狀態(tài)的線程不會被分配CPU時(shí)間薇宠,需要等待其他線程顯式地喚醒(Object.wait() Thread.join() LockSupport.park())偷办。

④、限期等待(Time Waiting):這種狀態(tài)線程也不會被分配CPU時(shí)間澄港,但不需要等待其他線程顯式地喚醒,一定時(shí)間會由系統(tǒng)自動喚醒(Thread.sleep() Object.wait(time) Thread.join(time) LockSupport.parkNanos() LockSupport.parkUnit())柄沮。

⑤回梧、阻塞(Blocked):阻塞與等待的區(qū)別在于,阻塞在等待獲取一個(gè)排它鎖祖搓,而等待狀態(tài)則等待一段時(shí)間或者喚醒動作發(fā)生狱意。

⑥、結(jié)束(Terminated):已終止線程狀態(tài)拯欧,線程已經(jīng)執(zhí)行結(jié)束详囤。

8、線程安全:當(dāng)多個(gè)線程訪問一個(gè)對象時(shí)镐作,不用考慮線程在運(yùn)行時(shí)的調(diào)度和交替執(zhí)行藏姐,也不需要進(jìn)行額外的同步,或其他的協(xié)調(diào)操作该贾,調(diào)用這個(gè)對象的行為都可以獲得正確的結(jié)果羔杨,那么這個(gè)對象就是線程安全的。java語言中的線程安全:不可變杨蛋、絕對線程安全兜材、相對線程安全、線程兼容和線程對立逞力。

①曙寡、不可變:final關(guān)鍵字,只要一個(gè)不可變對象被構(gòu)建出來寇荧,就不會改變举庶。(this引用逃逸情況例外,如在構(gòu)造方法還未初始化內(nèi)部靜態(tài)屬性砚亭,就已經(jīng)被其他線程使用灯变,有可能造成NPE)

②殴玛、絕對線程安全:如Vector,所有的方法都添加了synchronized添祸,但是不經(jīng)調(diào)控滚粟,有可能拋出錯(cuò)誤

③、相對線程安全:如Vector刃泌、HashTable等

④凡壤、線程兼容:需要使用同步手段實(shí)現(xiàn)線程安全

⑤、線程對立:無論是否采取同步措施耙替,都無法在多線程環(huán)境中并發(fā)使用亚侠,如Thread類的suspend()和resume()

9、鎖優(yōu)化:

①俗扇、自旋鎖:掛起線程和恢復(fù)線程都需要轉(zhuǎn)入內(nèi)核態(tài)完成硝烂,這些操作為并發(fā)帶來了很大的壓力。很大共享數(shù)據(jù)的鎖定狀態(tài)只會持續(xù)很短一段時(shí)間铜幽,讓請求鎖的線程暫不放棄CPU時(shí)間滞谢,而進(jìn)行一個(gè)忙循環(huán),等待鎖釋放除抛。

②狮杨、鎖消除:指虛擬機(jī)即時(shí)編輯器在運(yùn)行時(shí),對一些代碼上要求同步到忽,但被檢測到不可能存在共享數(shù)據(jù)競爭的鎖進(jìn)行消除橄教。

③、鎖粗化:在存在鎖競爭是一般推薦將同步代碼塊限制盡量小喘漏,為了線程盡可能拿到鎖护蝶。但當(dāng)一系列操作都對同一個(gè)對象反復(fù)加鎖和解鎖,則導(dǎo)致不必要的開銷陷遮,存在這種情況一般會將范圍擴(kuò)展到整個(gè)操作序列滓走,實(shí)現(xiàn)一次加解鎖。

④帽馋、輕量級鎖:HotSpot虛擬機(jī)的對象頭分兩部分信息搅方,其中一部分存儲對象自身的運(yùn)行時(shí)數(shù)據(jù)(另一部分時(shí)類元數(shù)據(jù)),如哈希碼绽族、GC分代年齡等姨涡,稱為Mark Word,是實(shí)現(xiàn)輕量級鎖的關(guān)鍵吧慢。輕量級鎖的依據(jù)是對于絕大部分鎖涛漂,在整個(gè)同步周期內(nèi)都不存在競爭。輕量級鎖的上鎖過程:如果對象沒有被鎖定,虛擬機(jī)在棧幀上建立一個(gè)所記錄(Lock Record)匈仗,存儲當(dāng)前對象的Mark Word拷貝瓢剿,然后虛擬機(jī)嘗試CAS操作將對象Mark Word更新為指向Lock Record的指針。如果操作失敗悠轩,先檢查是否已經(jīng)在當(dāng)前棧幀间狂,如果在可直接進(jìn)入同步代碼塊,否則已經(jīng)被其他線程搶占火架。如果有兩條以上的線程競爭同一個(gè)鎖鉴象,輕量級鎖就不再有效,要膨脹為重量級鎖何鸡,等待鎖的線程會進(jìn)入阻塞狀態(tài)纺弊。

⑤、偏向鎖:當(dāng)鎖對象第一次被線程獲取,即設(shè)置為偏向模式(01),同時(shí)使用CAS操作把獲取這個(gè)鎖的線程ID記錄在Mark Word中,操作成功,后續(xù)每次進(jìn)入同步塊時(shí)馁启,都不必進(jìn)行同步操作。如果另一個(gè)線程嘗試獲取鎖秕磷,偏向模式結(jié)束曲秉,恢復(fù)到未鎖定或輕量級鎖。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末熊赖,一起剝皮案震驚了整個(gè)濱河市来屠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌震鹉,老刑警劉巖俱笛,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異传趾,居然都是意外死亡迎膜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門浆兰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來磕仅,“玉大人,你說我怎么就攤上這事簸呈¢哦” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵蜕便,是天一觀的道長劫恒。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么两嘴? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任丛楚,我火速辦了婚禮,結(jié)果婚禮上憔辫,老公的妹妹穿的比我還像新娘趣些。我一直安慰自己,他們只是感情好螺垢,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布喧务。 她就那樣靜靜地躺著,像睡著了一般枉圃。 火紅的嫁衣襯著肌膚如雪功茴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天孽亲,我揣著相機(jī)與錄音坎穿,去河邊找鬼。 笑死返劲,一個(gè)胖子當(dāng)著我的面吹牛玲昧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播篮绿,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼孵延,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了亲配?” 一聲冷哼從身側(cè)響起尘应,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吼虎,沒想到半個(gè)月后犬钢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡思灰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年玷犹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洒疚。...
    茶點(diǎn)故事閱讀 40,110評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡歹颓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拳亿,到底是詐尸還是另有隱情晴股,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布肺魁,位于F島的核電站电湘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜寂呛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一怎诫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贷痪,春花似錦幻妓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至舱沧,卻和暖如春妹沙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背熟吏。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工距糖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人牵寺。 一個(gè)月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓悍引,卻偏偏與公主長得像,于是被迫代替她去往敵國和親帽氓。 傳聞我的和親對象是個(gè)殘疾皇子趣斤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 操作系統(tǒng)概論 操作系統(tǒng)的概念 操作系統(tǒng)是指控制和管理計(jì)算機(jī)的軟硬件資源,并合理的組織調(diào)度計(jì)算機(jī)的工作和資源的分配黎休,...
    野狗子嗷嗷嗷閱讀 11,933評論 3 34
  • 1. 基礎(chǔ)知識 1.1唬渗、 基本概念、 功能 馮諾伊曼體系結(jié)構(gòu)1奋渔、計(jì)算機(jī)處理的數(shù)據(jù)和指令一律用二進(jìn)制數(shù)表示2、順序執(zhí)...
    yunpiao閱讀 5,316評論 1 22
  • 除了充分利用計(jì)算機(jī)處理器的能力外壮啊,一個(gè)服務(wù)端同時(shí)對多個(gè)客戶端提供服務(wù)則是另一個(gè)更具體的并發(fā)應(yīng)用場景嫉鲸。衡量一個(gè)服務(wù)性...
    胡二囧閱讀 1,340評論 0 12
  • 1.內(nèi)存的頁面置換算法 (1)最佳置換算法(OPT)(理想置換算法):從主存中移出永遠(yuǎn)不再需要的頁面狸眼;如無這樣的...
    杰倫哎呦哎呦閱讀 3,254評論 1 9
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,103評論 1 32