日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

java 等待几秒_Java并发编程synchronized相关面试题总结

發(fā)布時(shí)間:2025/3/19 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 等待几秒_Java并发编程synchronized相关面试题总结 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

說(shuō)說(shuō)自己對(duì)于synchronized關(guān)鍵字的了解

synchronized關(guān)鍵字用于解決多個(gè)線程之間訪問(wèn)資源的同步性,synchronized關(guān)鍵字可以保證被它修飾的方法或者代碼塊在任意時(shí)刻只能有一個(gè)線程執(zhí)行。

值得注意的是,在Java早期,JDK1.6之前,synchronized屬于重量級(jí)鎖,效率低下。

原因在于:

監(jiān)視器鎖【monitor】依賴于底層操作系統(tǒng)的Mutex Lock實(shí)現(xiàn),Java的線程是映射到操作系統(tǒng)的原生線程之上的。如果要掛起或喚醒一個(gè)線程,都需要操作系統(tǒng)幫忙完成,而操作系統(tǒng)實(shí)現(xiàn)線程之間的切換時(shí)需要從用戶態(tài)轉(zhuǎn)化到內(nèi)核態(tài),需要消耗比較長(zhǎng)的時(shí)間。

但是,JDK1.6之后,Java官方從JVM層面對(duì)synchronized關(guān)鍵字進(jìn)行了較大的優(yōu)化,效率不可同日而語(yǔ)。主要的優(yōu)化有:自旋鎖、適應(yīng)性自旋鎖、鎖消除、鎖粗化、偏向鎖、輕量級(jí)鎖等技術(shù)來(lái)減少鎖操作的開銷。

synchronized關(guān)鍵字的三種使用

  • 修飾實(shí)例方法:作用于當(dāng)前對(duì)象實(shí)例加鎖,進(jìn)入同步代碼前要獲得?當(dāng)前對(duì)象實(shí)例的鎖。

  • 修飾靜態(tài)方法:?也就是給當(dāng)前類加鎖,會(huì)作用于類的所有對(duì)象實(shí)例 ,進(jìn)入同步代碼前要獲得?當(dāng)前 class 的鎖。

  • 注意:靜態(tài)成員不屬于任何一個(gè)實(shí)例對(duì)象,是類成員!因此,一個(gè)線程A調(diào)用一個(gè)實(shí)例對(duì)象的非靜態(tài)synchronized方法,一個(gè)線程B調(diào)用這個(gè)實(shí)例對(duì)象的所屬類的靜態(tài)synchronized方法,是被允許的。?因?yàn)樵L問(wèn)靜態(tài) synchronized 方法占用的鎖是當(dāng)前類的鎖,而訪問(wèn)非靜態(tài) synchronized 方法占用的鎖是當(dāng)前實(shí)例對(duì)象鎖。

  • 修飾代碼塊?:給括號(hào)內(nèi)配置的對(duì)象加鎖。synchronized(this|object) 表示進(jìn)入同步代碼庫(kù)前要獲得給定對(duì)象的鎖。synchronized(類.class) 表示進(jìn)入同步代碼前要獲得?當(dāng)前 class 的鎖。

  • synchronized關(guān)鍵字的底層原理

    通過(guò)對(duì).class文件的編譯可以發(fā)現(xiàn):

    • 同步方法通過(guò)ACC_SYNCHRONIZED修飾。

    • 代碼塊同步使用monitorenter和monitorexit兩個(gè)指令實(shí)現(xiàn)。

    雖然兩者實(shí)現(xiàn)細(xì)節(jié)不同,但其實(shí)本質(zhì)上都是JVM基于進(jìn)入和退出Monitor對(duì)象來(lái)實(shí)現(xiàn)同步,JVM的要求如下:

    • monitorenter指令會(huì)在編譯后插入到同步代碼塊的開始位置,而monitorexit則會(huì)插入到方法結(jié)束和異常處。

    • 每個(gè)對(duì)象都有一個(gè)monitor與之關(guān)聯(lián),且當(dāng)一個(gè)monitor被持有之后,他會(huì)處于鎖定狀態(tài)。

    • 線程執(zhí)行到monitorenter時(shí),會(huì)嘗試獲取對(duì)象對(duì)應(yīng)monitor的所有權(quán)。

    • 在獲取鎖時(shí),如果對(duì)象沒被鎖定,或者當(dāng)前線程已經(jīng)擁有了該對(duì)象的鎖(可重進(jìn)入,不會(huì)鎖死自己),將鎖計(jì)數(shù)器加一,執(zhí)行monitorexit時(shí),鎖計(jì)數(shù)器減一,計(jì)數(shù)為零則鎖釋放。

    • 獲取對(duì)象鎖失敗,則當(dāng)前線程陷入阻塞,直到對(duì)象鎖被另外一個(gè)線程釋放。

    JDK1.6之后對(duì)synchronized關(guān)鍵字進(jìn)行的優(yōu)化

    優(yōu)化:偏向鎖,輕量級(jí)鎖,自旋鎖,適應(yīng)性自旋鎖,鎖消除,鎖粗化。

    鎖主要存在的四種狀態(tài),依次是:無(wú)鎖狀態(tài)、偏向鎖狀態(tài)、輕量級(jí)鎖狀態(tài)、重量級(jí)鎖狀態(tài),他們會(huì)隨著競(jìng)爭(zhēng)的激烈而逐漸升級(jí)。注意鎖可以升級(jí)不可降級(jí),這種策略是為了提高獲得鎖和釋放鎖的效率。

    Java對(duì)象頭的組成

    鎖存在于Java對(duì)象頭里,對(duì)象頭的組成部分:

    • Mark Word:存儲(chǔ)對(duì)象的hashCode或鎖信息等。

    • Class Metadata Address:存儲(chǔ)到對(duì)象類型數(shù)據(jù)的指針。

    • Array length:數(shù)組的長(zhǎng)度(如果當(dāng)前對(duì)象是數(shù)組)

    Java對(duì)象頭又存在于Java堆中,堆內(nèi)存分為三部分:對(duì)象頭,實(shí)例數(shù)據(jù)和對(duì)齊填充。

    MarkWord的組成

    Java對(duì)象頭的MardWord中記錄了對(duì)象和鎖的相關(guān)信息,無(wú)鎖狀態(tài)下,Java對(duì)象頭里的Mark Word里默認(rèn)存儲(chǔ)對(duì)象的HashCode、分代年齡和鎖標(biāo)記位。在64位的JVM中,Mark Word為64 bit。

    在運(yùn)行期間Mark Word里存儲(chǔ)的數(shù)據(jù)會(huì)隨著鎖標(biāo)志位的變化而變化。鎖升級(jí)的功能也主要靠MarkWord中鎖標(biāo)志位和是否偏向鎖標(biāo)志完成。

    鎖升級(jí)的過(guò)程

    鎖升級(jí)的過(guò)程:無(wú)鎖,偏向鎖,輕量級(jí)鎖,重量級(jí)鎖

    偏向鎖

    HotSpot的作者經(jīng)過(guò)研究發(fā)現(xiàn),大多數(shù)情況下,鎖不僅不存在多線程競(jìng)爭(zhēng),而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價(jià)更低而引入了偏向鎖。

    偏向鎖的適用場(chǎng)景

    偏向鎖主要用于優(yōu)化:同一線程多次申請(qǐng)同一個(gè)鎖的競(jìng)爭(zhēng),在某些情況下,大部分時(shí)間都是同一個(gè)線程競(jìng)爭(zhēng)鎖資源的。

    偏向鎖的加鎖

    主要流程:當(dāng)一個(gè)線程訪問(wèn)同步塊并獲取鎖時(shí),會(huì)在對(duì)象頭和棧幀中的鎖記錄里存儲(chǔ)鎖偏向的線程ID,以后該線程在進(jìn)入和退出同步塊時(shí)不需要進(jìn)行CAS操作來(lái)加鎖和解鎖,只需簡(jiǎn)單地測(cè)試一下對(duì)象頭的Mark Word里是否存儲(chǔ)著指向當(dāng)前線程的偏向鎖。

    • 如果測(cè)試成功,表示線程已經(jīng)獲得了鎖。

    • 如果測(cè)試失敗,則需要再測(cè)試一下Mark Word中偏向鎖的標(biāo)識(shí)是否設(shè)置成1(表示當(dāng)前是偏向鎖):如果沒有設(shè)置,則使用CAS競(jìng)爭(zhēng)鎖。如果設(shè)置了,則嘗試使用CAS將對(duì)象頭的偏向鎖指向當(dāng)前線程。

    偏向鎖的撤銷

    一旦出現(xiàn)其他線程競(jìng)爭(zhēng)鎖資源時(shí),偏向鎖就會(huì)被撤銷。偏向鎖的撤銷可能需要等待全局安全點(diǎn)【在這個(gè)時(shí)間點(diǎn)上沒有正在執(zhí)行的字節(jié)碼】。

    • 首先暫停持有該鎖的線程,然后檢查持有偏向鎖的線程是否活著,如果線程不處于活動(dòng)狀態(tài),則將對(duì)象頭設(shè)置成無(wú)鎖狀態(tài)。

    • 如果持有偏向鎖的線程仍然活著,擁有偏向鎖的棧會(huì)被執(zhí)行,遍歷偏向?qū)ο蟮逆i記錄,棧中的鎖記錄和對(duì)象頭的Mark Word要么重新偏向于其他線程,要么恢復(fù)到無(wú)鎖或者標(biāo)記對(duì)象不適合作為偏向鎖,最后喚醒在暫停的線程。

    偏向鎖的關(guān)閉

    偏向鎖在Java 6和Java 7里是默認(rèn)啟用的,但是它在應(yīng)用程序啟動(dòng)幾秒鐘之后才激活,如有必要可以使用JVM參數(shù)來(lái)關(guān)閉延遲:-XX:BiasedLockingStartupDelay=0。

    如果說(shuō)通常處于競(jìng)爭(zhēng)狀態(tài),可以通過(guò)- XX:-UseBiasedLocking=false,進(jìn)入輕量級(jí)鎖狀態(tài)。

    輕量級(jí)鎖

    如偏向鎖存在,如有另一線程競(jìng)爭(zhēng)鎖,且對(duì)象頭MarkWord中的線程ID與當(dāng)前線程ID不同,則該線程將會(huì)嘗試CAS操作獲取鎖,獲取失敗,代表鎖存在競(jìng)爭(zhēng),偏向鎖向輕量級(jí)鎖升級(jí)。

    輕量級(jí)鎖的加鎖

    • 線程在執(zhí)行同步塊之前,JVM會(huì)先在當(dāng)前線程的棧臺(tái)中創(chuàng)建用于存儲(chǔ)鎖記錄的空間【Displaced Mark Word】,并將對(duì)象頭中的Mark Word復(fù)制到鎖記錄中。

    • 然后線程嘗試使用CAS將對(duì)象頭中的Mark Word替換為指向鎖記錄的指針。替換成功,則當(dāng)前線程獲得鎖。替換失敗,表示其他線程競(jìng)爭(zhēng)鎖,當(dāng)前線程嘗試使用自旋來(lái)獲取鎖。

    輕量級(jí)鎖的解鎖

    • 使用原子的CAS操作將【Displaced Mark Word】替換回對(duì)象頭。替換成功,表示沒有競(jìng)爭(zhēng)發(fā)生。替換失敗,表示當(dāng)前鎖存在競(jìng)爭(zhēng),鎖就會(huì)膨脹成重量級(jí)鎖。

    輕量級(jí)鎖的適用場(chǎng)景

    線程交替執(zhí)行同步塊,絕大部分的鎖在整個(gè)同步周期內(nèi)都不存在長(zhǎng)時(shí)間的競(jìng)爭(zhēng)。

    鎖的優(yōu)缺點(diǎn)對(duì)比

    鎖優(yōu)點(diǎn)缺點(diǎn)適用場(chǎng)景偏向鎖加鎖和解鎖不需要額外的消耗,和執(zhí)行非同步方法比僅存在納秒級(jí)的差距。如果線程間存在鎖競(jìng)爭(zhēng), 會(huì)帶來(lái)額外的鎖撤銷的消耗。適用于只有一個(gè)線程訪問(wèn)同步塊場(chǎng)景。輕量級(jí)鎖競(jìng)爭(zhēng)的線程不會(huì)阻塞,提高了程序的響應(yīng)速度。如果始終得不到所競(jìng)爭(zhēng)的線程使用自旋會(huì)消耗CPU。追求響應(yīng)時(shí)間。同步塊執(zhí)行速度非常快。重量級(jí)鎖線程競(jìng)爭(zhēng)不使用自旋,不會(huì)消耗CPU。線程阻塞,響應(yīng)時(shí)間緩慢。追求吞吐量。同步塊執(zhí)行速度較長(zhǎng)。

    總結(jié)

  • JVM在JDK 1.6中引入了分級(jí)鎖機(jī)制來(lái)優(yōu)化synchronized

  • 當(dāng)一個(gè)線程獲取鎖時(shí),首先對(duì)象鎖成為一個(gè)偏向鎖這是為了避免在同一線程重復(fù)獲取同一把鎖時(shí),用戶態(tài)和內(nèi)核態(tài)頻繁切換

  • 如果有多個(gè)線程競(jìng)爭(zhēng)鎖資源,鎖將會(huì)升級(jí)為輕量級(jí)鎖這適用于在短時(shí)間內(nèi)持有鎖,且分鎖交替切換的場(chǎng)景輕量級(jí)鎖還結(jié)合了自旋鎖來(lái)避免線程用戶態(tài)與內(nèi)核態(tài)的頻繁切換

  • 如果鎖競(jìng)爭(zhēng)太激烈(自旋鎖失敗),同步鎖會(huì)升級(jí)為重量級(jí)鎖

  • 優(yōu)化synchronized同步鎖的關(guān)鍵:減少鎖競(jìng)爭(zhēng)應(yīng)該盡量使synchronized同步鎖處于輕量級(jí)鎖或偏向鎖,這樣才能提高synchronized同步鎖的性能常用手段減少鎖粒度:降低鎖競(jìng)爭(zhēng)減少鎖的持有時(shí)間,提高synchronized同步鎖在自旋時(shí)獲取鎖資源的成功率,避免升級(jí)為重量級(jí)鎖

  • 在鎖競(jìng)爭(zhēng)激烈時(shí),可以考慮禁用偏向鎖和禁用自旋鎖

  • synchronized關(guān)鍵字與ReentrantLock的區(qū)別

    共同點(diǎn)

    • 都是可重入鎖:自己可以再次獲取自己的內(nèi)部鎖【避免一個(gè)線程獲取鎖之后,再次嘗試獲取鎖時(shí)造成的死鎖】。同一線程每次獲取鎖,計(jì)數(shù)器加一,釋放鎖,計(jì)數(shù)器減一,計(jì)數(shù)為0,代表完全釋放該鎖。

    不同點(diǎn)

    • synchronized依賴于JVM實(shí)現(xiàn),ReentrantLock依賴于API。

    • 相比synchronized,ReentrantLock增加了一些高級(jí)功能。主要來(lái)說(shuō)主要有三點(diǎn):等待可中斷?: ReentrantLock提供了一種能夠中斷等待鎖的線程的機(jī)制,通過(guò) lock.lockInterruptibly() 來(lái)實(shí)現(xiàn)這個(gè)機(jī)制。也就是說(shuō)正在等待的線程可以選擇放棄等待,改為處理其他事情。可實(shí)現(xiàn)公平鎖?: ReentrantLock可以指定是公平鎖還是非公平鎖。而synchronized只能是非公平鎖。所謂的公平鎖就是先等待的線程先獲得鎖。ReentrantLock默認(rèn)情況是非公平的,可以通過(guò) ReentrantLock類的ReentrantLock(boolean fair)構(gòu)造方法來(lái)制定是否是公平的。可實(shí)現(xiàn)選擇性通知(鎖可以綁定多個(gè)條件): synchronized關(guān)鍵字與wait()和notify()/notifyAll()方法相結(jié)合可以實(shí)現(xiàn)等待/通知機(jī)制。ReentrantLock類當(dāng)然也可以實(shí)現(xiàn),但是需要借助于Condition接口與newCondition()方法。

    如果感覺本文對(duì)你有幫助,點(diǎn)贊再看支持一下

    總結(jié)

    以上是生活随笔為你收集整理的java 等待几秒_Java并发编程synchronized相关面试题总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。