Java鎖學(xué)習(xí)筆記

  • synchronized可以保證方法或者代碼塊在運(yùn)行時,同一時刻只有一個方法可以進(jìn)入到臨界區(qū)件余,同時它還可以保證共享變量的內(nèi)存可見性蚁堤。
  • Java中每一個對象都可以作為鎖,普通同步方法刽辙,鎖是當(dāng)前對象的實例窥岩;靜態(tài)同步方法,鎖是當(dāng)前類的class對象宰缤;靜態(tài)代碼塊颂翼,鎖是括號里面的對象。
  • 同步代碼塊:同步代碼塊是使用monitorenter和monitorexit指令實現(xiàn)的慨灭;monitorenter指令插入到同步代碼塊的開始位置朦乏,monitorexit指定插入到同步代碼塊的結(jié)束為止,JVM保證每一個monitorenter和monitorexit成對出現(xiàn)氧骤。任何U對象都有一個monitor與之相對應(yīng)呻疹,當(dāng)且一個monitor被持有之后,他將處于鎖定狀態(tài)筹陵。線程執(zhí)行到monitorenter指令時刽锤,將會嘗試獲取對象所對應(yīng)的monitor所有權(quán),即嘗試獲取對象的鎖朦佩。
  • 同步方法:synchronized方法會被翻譯成普通的方法調(diào)用和返回指令并思,如:invokevirtual、areturn指令语稠,在JVM字節(jié)碼層面并沒有任何特別的指令 來實現(xiàn)被synchronized修飾的方法宋彼,而是在Class文件的方法表中將該方法的access_flags字段中的synchronized標(biāo)志物置位1,表示該方法是同步方法使用調(diào)用該方法的對象或該方法所屬的Class在JVM的內(nèi)部對象表示Klass作為鎖對象。
  • Java對象頭:synchronized用的鎖存在Java對象頭里面输涕。Hotspot虛擬機(jī)的對象頭主要包括兩部分?jǐn)?shù)據(jù)音婶,Mark Word(標(biāo)記字段)、Klass Pointer(類型指針)占贫。其中Klass Point是對象指向它的類元數(shù)據(jù)的指針桃熄,虛擬機(jī)通過這個指針來確定這個對象是哪個類的實例先口,Mark Word用于存儲對象自身運(yùn)行時數(shù)據(jù)型奥,它是實現(xiàn)輕量級鎖和偏向鎖的關(guān)鍵。
  • Monitor:Monitor可以理解為一個同步工具碉京,也可以描述為一種同步機(jī)制厢汹,也是一個對象,每個Java對象都有可能成為一個Monitor谐宙,因為每一個Java對象本身就帶有一把看不見的鎖烫葬,它叫做內(nèi)部鎖或者M(jìn)onitor鎖。Monitor是線程私有的數(shù)據(jù)結(jié)構(gòu)凡蜻,每個線程都有一個可用的monitor record列表搭综,同時還有一個全局的可用列表。每一個被鎖住的對象都會和一個monitor關(guān)聯(lián)(對象頭的MarkWord中的LockWord指向monitor的起始地址)划栓,同時monitor中還有一個Owner字段存放擁有該鎖的線程的唯一標(biāo)識兑巾,表示該鎖被這個線程占用。
  • 自旋鎖:自旋鎖讓線程不會被立即掛起忠荞,而是讓線程執(zhí)行一段時間的循環(huán)去等待蒋歌,看持有鎖的線程是否會很快釋放鎖。自旋等待不能代替阻塞委煤,在JDK1.6中默認(rèn)開啟堂油,同時自旋的默認(rèn)次數(shù)為10次,可以通過-XX:PreBlockSpin來調(diào)整次數(shù)碧绞。
  • 適應(yīng)自旋鎖:所謂自適應(yīng)就意味著自旋的次數(shù)不是固定的府框,它由前一次在同一個鎖上的自旋時間以及鎖的擁有著的狀態(tài)決定。具體為如果這一次自旋成功了讥邻,那么下次自旋的次數(shù)就會加多迫靖。反之,如果對于某個鎖很少有自旋成功的计维,那么以后這個鎖的自旋次數(shù)就會減少或者省略自旋過程直接掛起袜香。
  • 鎖消除:為了保證數(shù)據(jù)的完整性,我們在進(jìn)行操作時需要對這部分操作進(jìn)行同步控制鲫惶,但是在有些情況下蜈首,JVM檢測到不可能存在共享數(shù)據(jù)競爭,這時JVM會對這些同步鎖進(jìn)行鎖消除。鎖消除的依據(jù)是逃逸分析的數(shù)據(jù)支撐欢策。
  • 鎖粗化:將多個連續(xù)的加鎖吆寨、解鎖操作連接在一起,擴(kuò)展成一個范圍更大的鎖踩寇。
  • 輕量級鎖:引入輕量級鎖的主要目的是在沒有多線程競爭的前提下啄清,減少傳統(tǒng)的重量級鎖使用操作系統(tǒng)互斥量產(chǎn)生的性能消耗。當(dāng)關(guān)閉偏向鎖功能或者多個線程競爭偏向鎖導(dǎo)致偏向鎖升級為輕量級鎖俺孙,則會嘗試獲取輕量級鎖辣卒。對于輕量級鎖,其性能提升的依據(jù)是"對于絕大部分鎖睛榄,在整個生命周期內(nèi)都是不會存在競爭的"荣茫,如果打破這個依據(jù),除了互斥的開銷外场靴,還有額外的CAS操作啡莉,因此在有多線程競爭的情況下,輕量級鎖比重量級鎖更慢旨剥。
  • 偏向鎖:引入偏向鎖的主要目的是為了在無多線程競爭的情況下盡量減少不必要的輕量級鎖執(zhí)行路徑咧欣。偏向鎖不會主動釋放,而是等待其它的線程來競爭鎖才會釋放轨帜,偏向鎖的撤銷需要等待全局安全點(diǎn)(這個時間點(diǎn)是沒有正在執(zhí)行的代碼)魄咕。
  • 重量級鎖:重量級鎖通過對象內(nèi)部的監(jiān)視器(monitor)實現(xiàn),其中monitor的本質(zhì)是依賴于底層操作系統(tǒng)的Mutex Lock實現(xiàn)阵谚,操作系統(tǒng)實現(xiàn)線程間的切換需要從用戶態(tài)到內(nèi)核態(tài)的裝換蚕礼,切換成本給常高。
  • volatile的定義:Java編程語言允許線程訪問共享變量梢什,為了確保共享變量能被準(zhǔn)確和一致的更新奠蹬,線程應(yīng)該確保通過排它鎖單獨(dú)獲得這個變量。
  • 解決緩存一致性的方案:
    1. 通過在總線加LOCK#鎖的方式嗡午。
      采用一種獨(dú)占的方式來實現(xiàn)囤躁,只能有一個CPU能夠運(yùn)行,其它CPU都阻塞荔睹,效率低狸演。
    2. 通過緩存一致性協(xié)議。
      緩存一致性協(xié)議(MESI協(xié)議)它確保每個緩存中使用的共享變量的副本是一致的僻他。其核心思想:當(dāng)某個CPU在寫數(shù)據(jù)時宵距,如果發(fā)現(xiàn)操作的變量是共享變量,則會通知其它CPU告知該變量的副本是無效的吨拗,因此其它CPU在讀取該變量時满哪,發(fā)現(xiàn)其無效會重新從主存中加載該數(shù)據(jù)婿斥。
  • 原子性:一個操作或者多個操作,要么全部執(zhí)行并且執(zhí)行的過程不會被任何因素打斷哨鸭,要么就都不執(zhí)行民宿。
  • 可見性:當(dāng)多個線程訪問同一個變量時,一個線程修改了這個變量的值像鸡,其它線程能夠立刻看到修改后的值活鹰。
  • 有序性:程序執(zhí)行是順序按照代碼的先后順序執(zhí)行。
  • volatile可以保證線程的可見性并提供了一定的有序性只估,但無法保證原子性志群。在JVM底層volatile采用"內(nèi)存屏障"來實現(xiàn),匯編中會使用lock前綴指令形成內(nèi)存屏障仅乓。volatile使用b必須滿足兩個條件:(1)對變量的寫操作不依賴當(dāng)前值赖舟。(2)該變量沒有包含在具有其他變量的不變式中蓬戚。
  • volatile實現(xiàn)原理:
    • 在每一個volatile寫操作前面插入一個StoreStore屏障夸楣。
    • 在每一個volatile寫操作后面插入一個StoreLoad屏障。
    • 在每一個volatile讀操作后面插入一個LoadLoad屏障子漩。
    • 在每一個volatile讀操作后面插入一個LoadStore屏障豫喧。
      StoreStore屏障可以保證在volatile寫之前,其前面的所有普通寫操作都已經(jīng)刷新到主內(nèi)存中幢泼。
      StoreLoad屏障的作用是避免volatile寫與后面可能有的volatile讀/寫操作重排序紧显。
      LoadLoad屏障用來禁止處理器把上面的volatile讀與下面的普通讀重排序。
      LoadStore屏障用來禁止處理器把上面的volatile讀與下面的普通寫重排序缕棵。
  • happens-before:在JMM中孵班,如果一個操作執(zhí)行的結(jié)果需要對另一個操作可見,那么這兩個操作之間必須存在happens-before關(guān)系招驴。它是判斷數(shù)據(jù)是否存在競爭篙程、線程是否安全的主要依據(jù),保證了多線程環(huán)境下的可見性别厘。
  • as-if-serial語義:所有的操作均可以為了優(yōu)化而被重排序虱饿,但是必須要保證重排序后執(zhí)行的結(jié)果不能被改變,編譯器触趴、runtime氮发、處理器都必須遵守as-if-serial語義。注意as-if-serial只保證單線程環(huán)境冗懦,多線程環(huán)境下無效爽冕。
  • 重排序不會影響單線程環(huán)境的運(yùn)行結(jié)果,但會破壞多線程的執(zhí)行語義披蕉。
  • DCL(Double Check Lock)多重檢測鎖定颈畸。
  • JVM在類初始化階段會獲取一個鎖前塔,這個鎖可以同步多個線程對同一個類的初始化。
  • AQS承冰,AbstractQueuedSynchronizer华弓,即隊列同步器。它是構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架(如ReentrantLock困乒、ReentrantReadWriteLock寂屏、Semaphore等),JUC并發(fā)包的作者(Doug Lea)期望它能夠成為實現(xiàn)大部分同步需求的基礎(chǔ)娜搂。它是JUC并發(fā)包中的核心基礎(chǔ)組件迁霎。
    AQS解決了子啊實現(xiàn)同步器時涉及當(dāng)?shù)拇罅考?xì)節(jié)問題,例如獲取同步狀態(tài)百宇、FIFO同步隊列考廉。基于AQS來構(gòu)建同步器可以帶來很多好處携御。它不僅能夠極大地減少實現(xiàn)工作昌粤,而且也不必處理在多個位置上發(fā)生的競爭問題。
    在基于AQS構(gòu)建的同步器中啄刹,只能在一個時刻發(fā)生阻塞涮坐,從而降低上下文切換的開銷,提高了吞吐量誓军。同時在設(shè)計AQS時充分考慮了可伸縮行袱讹,因此J.U.C中所有基于AQS構(gòu)建的同步器均可以獲得這個優(yōu)勢。
    AQS的主要使用方式是繼承昵时,子類通過繼承同步器并實現(xiàn)它的抽象方法來管理同步狀態(tài)捷雕。
    AQS使用一個int類型的成員變量state來表示同步狀態(tài),當(dāng)state>0時表示已經(jīng)獲取了鎖壹甥,當(dāng)state = 0時表示釋放了鎖救巷。它提供了三個方法(getState()、setState(int newState)盹廷、compareAndSetState(int expect,int update))來對同步狀態(tài)state進(jìn)行操作征绸,當(dāng)然AQS可以確保對state的操作是安全的。
    AQS通過內(nèi)置的FIFO同步隊列來完成資源獲取線程的排隊工作俄占,如果當(dāng)前線程獲取同步狀態(tài)失敼艿 (鎖)時,AQS則會將當(dāng)前線程以及等待狀態(tài)等信息構(gòu)造成一個節(jié)點(diǎn)(Node)并將其加入同步隊列缸榄,同時會阻塞當(dāng)前線程渤弛,當(dāng)同步狀態(tài)釋放時,則會把節(jié)點(diǎn)中的線程喚醒甚带,使其再次嘗試獲取同步狀態(tài)她肯。
    AQS主要提供了如下一些方法:
    • getState():返回同步狀態(tài)的當(dāng)前值佳头;
    • setState(int newState):設(shè)置當(dāng)前同步狀態(tài);
    • compareAndSetState(int expect, int update):使用CAS設(shè)置當(dāng)前狀態(tài)晴氨,該方法能夠保證狀態(tài)設(shè)置的原子性康嘉;
    • tryAcquire(int arg):獨(dú)占式獲取同步狀態(tài),獲取同步狀態(tài)成功后籽前,其他線程需要等待該線程釋放同步狀態(tài)才能獲取同步狀態(tài)亭珍;
    • tryRelease(int arg):獨(dú)占式釋放同步狀態(tài);
    • tryAcquireShared(int arg):共享式獲取同步狀態(tài)枝哄,返回值大于等于0則表示獲取成功肄梨,否則獲取失敗挠锥;
    • tryReleaseShared(int arg):共享式釋放同步狀態(tài)众羡;
    • isHeldExclusively():當(dāng)前同步器是否在獨(dú)占式模式下被線程占用,一般該方法表示是否被當(dāng)前線程所獨(dú)占蓖租;
    • acquire(int arg):獨(dú)占式獲取同步狀態(tài)粱侣,如果當(dāng)前線程獲取同步狀態(tài)成功,則由該方法返回菜秦,否則甜害,將會進(jìn)入同步隊列等待,該方法將會調(diào)用可重寫的tryAcquire(int arg)方法球昨;
    • acquireInterruptibly(int arg):與acquire(int arg)相同,但是該方法響應(yīng)中斷眨攘,當(dāng)前線程為獲取到同步狀態(tài)而進(jìn)入到同步隊列中主慰,如果當(dāng)前線程被中斷,則該方法會拋出InterruptedException異常并返回鲫售;
    • tryAcquireNanos(int arg,long nanos):超時獲取同步狀態(tài)共螺,如果當(dāng)前線程在nanos時間內(nèi)沒有獲取到同步狀態(tài),那么將會返回false情竹,已經(jīng)獲取則返回true藐不;
    • acquireShared(int arg):共享式獲取同步狀態(tài),如果當(dāng)前線程未獲取到同步狀態(tài)秦效,將會進(jìn)入同步隊列等待雏蛮,與獨(dú)占式的主要區(qū)別是在同一時刻可以有多個線程獲取到同步狀態(tài);
    • acquireSharedInterruptibly(int arg):共享式獲取同步狀態(tài)阱州,響應(yīng)中斷挑秉;
    • tryAcquireSharedNanos(int arg, long nanosTimeout)`:共享式獲取同步狀態(tài),增加超時限制苔货;
    • release(int arg):獨(dú)占式釋放同步狀態(tài)犀概,該方法會在釋放同步狀態(tài)之后立哑,將同步隊列中第一個節(jié)點(diǎn)包含的線程喚醒;
    • releaseShared(int arg):共享式釋放同步狀態(tài)姻灶;
  • CLH同步隊列是一個FIFO雙向隊列铛绰,AQS依賴它來完成同步狀態(tài)的管理,當(dāng)前線程如果獲取同步狀態(tài)失敗時产喉,AQS則會將當(dāng)前線程已經(jīng)等待狀態(tài)等信息構(gòu)造成一個節(jié)點(diǎn)(Node)并將其加入到CLH同步隊列至耻,同時會阻塞當(dāng)前線程,當(dāng)同步狀態(tài)釋放時镊叁,會把首節(jié)點(diǎn)喚醒(公平鎖)尘颓,使其再次嘗試獲取同步狀態(tài)。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晦譬,一起剝皮案震驚了整個濱河市疤苹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌敛腌,老刑警劉巖卧土,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異像樊,居然都是意外死亡尤莺,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進(jìn)店門生棍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颤霎,“玉大人,你說我怎么就攤上這事涂滴∮呀矗” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵柔纵,是天一觀的道長缔杉。 經(jīng)常有香客問我,道長搁料,這世上最難降的妖魔是什么或详? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮郭计,結(jié)果婚禮上霸琴,老公的妹妹穿的比我還像新娘。我一直安慰自己拣宏,他們只是感情好沈贝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著勋乾,像睡著了一般宋下。 火紅的嫁衣襯著肌膚如雪嗡善。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天学歧,我揣著相機(jī)與錄音罩引,去河邊找鬼。 笑死枝笨,一個胖子當(dāng)著我的面吹牛袁铐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播横浑,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼剔桨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了徙融?” 一聲冷哼從身側(cè)響起洒缀,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎欺冀,沒想到半個月后树绩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡隐轩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年饺饭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片职车。...
    茶點(diǎn)故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡瘫俊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出提鸟,到底是詐尸還是另有隱情军援,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布称勋,位于F島的核電站,受9級特大地震影響涯竟,放射性物質(zhì)發(fā)生泄漏赡鲜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一庐船、第九天 我趴在偏房一處隱蔽的房頂上張望银酬。 院中可真熱鬧,春花似錦筐钟、人聲如沸揩瞪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春巧骚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背集索。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工袱饭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人妇菱。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓承粤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親闯团。 傳聞我的和親對象是個殘疾皇子辛臊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評論 2 355

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