溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JAVA的synchronized作用是什么

發(fā)布時間:2021-08-20 12:40:35 來源:億速云 閱讀:187 作者:chen 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“JAVA的synchronized作用是什么”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

目錄
  • 1、synchronized的作用

  • 2、synchronized的語法

  • 3、Monitor原理

  • 4、synchronized的原理

    • 4.1偏向鎖

    • 4.2輕量級鎖

    • 4.3鎖膨脹

    • 4.4重量級鎖

    • 4.5自旋鎖

    • 4.6鎖消除

    • 4.7鎖粗化

  • 5、鎖升級過程

    • 總結(jié)

      1、synchronized的作用

      為了避免臨界區(qū)的競態(tài)條件發(fā)生,有多種手段可以達(dá)到目的。

      • 阻塞式的解決方案:synchronized,Lock

      • 非阻塞式的解決方案:原子變量

      synchronized,即俗稱的【對象鎖】,它采用互斥的方式讓同一時刻至多只有一個線程能持有【對象鎖】,其它線程再想獲取這個【對象鎖】時就會阻塞住。這樣就能保證擁有鎖的線程可以安全的執(zhí)行臨界區(qū)內(nèi)的代碼,不用擔(dān)心線程上下文切換。

      synchronized的三個作用

      1. 原子性:確保線程互斥的訪問同步代碼

      2. 可見性:保證共享變量的修改能夠及時可見

      3. 有序性:有效解決重排序問題

      2、synchronized的語法

      class Test1{
          public synchronized void test() {
          }
      }
      //等價于
      class Test1{
          public void test() {
              //鎖的是當(dāng)前對象
              synchronized(this) {
              }
          }
      }
      class Test2{
          public synchronized static void test() {
          }
      }
      //等價于
      class Test2{
          public static void test() {
              //鎖的是類對象,類對象只有一個
              synchronized(Test2.class) {
              }
          }
      }

      3、Monitor原理

      Monitor 被翻譯為監(jiān)視器或管程

      每個 Java 對象都可以關(guān)聯(lián)一個 Monitor 對象,如果使用 synchronized 給對象上鎖(重量級)之后,該對象頭的 Mark Word 中就被設(shè)置指向 Monitor 對象的指針

      Monitor 結(jié)構(gòu)如下

      JAVA的synchronized作用是什么

      • 剛開始 Monitor 中 Owner 為 null

      • 當(dāng) Thread-2 執(zhí)行 synchronized(obj) 就會將 Monitor 的所有者 Owner 置為 Thread-2,Monitor中只能有一個 Owner

      • 在 Thread-2 上鎖的過程中,如果 Thread-3,Thread-4,Thread-5 也來執(zhí)行 synchronized(obj),就會進(jìn)入EntryList BLOCKED

      • Thread-2 執(zhí)行完同步代碼塊的內(nèi)容,然后喚醒 EntryList 中等待的線程來競爭鎖,競爭的時是非公平的

      • 圖中 WaitSet 中的 Thread-0,Thread-1 是之前獲得過鎖,但條件不滿足進(jìn)入 WAITING 狀態(tài)的線程

      注意:不加 synchronized 的對象不會關(guān)聯(lián)監(jiān)視器

      4、synchronized的原理

      通過對Java代碼進(jìn)行反編譯可知,Synchronized的語義底層是通過一個monitor的對象來完成,
      其實(shí)wait/notify等方法也依賴于monitor對象,這就是為什么只有在同步的塊或者方法中才能調(diào)用wait/notify等方法,否則會拋出java.lang.IllegalMonitorStateException的異常的原因。

      從JDK5引入了現(xiàn)代操作系統(tǒng)新增加的CAS原子操作( JDK5中并沒有對synchronized關(guān)鍵字做優(yōu)化,而是體現(xiàn)在J.U.C中,所以在該版本concurrent包有更好的性能 ),從JDK6開始,就對synchronized的實(shí)現(xiàn)機(jī)制進(jìn)行了較大調(diào)整,包括使用JDK5引進(jìn)的CAS自旋之外,還增加了自適應(yīng)的CAS自旋、鎖消除、鎖粗化、偏向鎖、輕量級鎖這些優(yōu)化策略。由于此關(guān)鍵字的優(yōu)化使得性能極大提高.

      鎖主要存在四種狀態(tài),依次是:無鎖狀態(tài)、偏向鎖狀態(tài)、輕量級鎖狀態(tài)、重量級鎖狀態(tài),鎖可以從偏向鎖升級到輕量級鎖,再升級的重量級鎖。但是鎖的升級是單向的,也就是說只能從低到高升級,不會出現(xiàn)鎖的降級。

      在 JDK 1.6 中默認(rèn)是開啟偏向鎖和輕量級鎖的,可以通過-XX:-UseBiasedLocking來禁用偏向鎖。

      4.1偏向鎖

      Java 6 中引入了偏向鎖來做進(jìn)一步優(yōu)化:只有第一次使用 CAS 將線程 ID 設(shè)置到對象的 Mark Word 頭,之后發(fā)現(xiàn) 這個線程 ID 是自己的就表示沒有競爭,不用重新 CAS。以后只要不發(fā)生競爭,這個對象就歸該線程所有。

      JAVA的synchronized作用是什么

      調(diào)用了對象的 hashCode,但偏向鎖的對象 MarkWord 中存儲的是線程 id,如果調(diào)用 hashCode 會導(dǎo)致偏向鎖被撤銷

      • 輕量級鎖會在鎖記錄中記錄 hashCode

      • 重量級鎖會在 Monitor 中記錄 hashCode

      4.2輕量級鎖

      輕量級鎖的使用場景:如果一個對象雖然有多線程要加鎖,但加鎖的時間是錯開的(也就是沒有競爭),那么可以使用輕量級鎖來優(yōu)化。
      輕量級鎖對使用者是透明的,即語法仍然是 synchronized。

      引入輕量級鎖的主要目的是 在沒有多線程競爭的前提下,減少傳統(tǒng)的重量級鎖使用操作系統(tǒng)互斥量產(chǎn)生的性能消耗。當(dāng)關(guān)閉偏向鎖功能或者多個線程競爭偏向鎖導(dǎo)致偏向鎖升級為輕量級鎖,則會嘗試獲取輕量級鎖。

      4.3鎖膨脹

      如果在嘗試加輕量級鎖的過程中,CAS 操作無法成功,這時一種情況就是有其它線程為此對象加上了輕量級鎖(有 競爭),這時需要進(jìn)行鎖膨脹,將輕量級鎖變?yōu)橹亓考夋i。


      4.4重量級鎖

      Synchronized是通過對象內(nèi)部的一個叫做 監(jiān)視器鎖(Monitor)來實(shí)現(xiàn)的。但是監(jiān)視器鎖本質(zhì)又是依賴于底層的操作系統(tǒng)的Mutex Lock來實(shí)現(xiàn)的。而操作系統(tǒng)實(shí)現(xiàn)線程之間的切換這就需要從用戶態(tài)轉(zhuǎn)換到核心態(tài),這個成本非常高,狀態(tài)之間的轉(zhuǎn)換需要相對比較長的時間,這就是為什么Synchronized效率低的原因。因此,這種依賴于操作系統(tǒng)Mutex Lock所實(shí)現(xiàn)的鎖我們稱之為 “重量級鎖”。

      4.5自旋鎖

      線程的阻塞和喚醒需要CPU從用戶態(tài)轉(zhuǎn)為核心態(tài),頻繁的阻塞和喚醒對CPU來說是一件負(fù)擔(dān)很重的工作,勢必會給系統(tǒng)的并發(fā)性能帶來很大的壓力。同時我們發(fā)現(xiàn)在許多應(yīng)用上面,對象鎖的鎖狀態(tài)只會持續(xù)很短一段時間,為了這一段很短的時間頻繁地阻塞和喚醒線程是非常不值得的。

      所以引入自旋鎖,何謂自旋鎖?

      所謂自旋鎖,就是指當(dāng)一個線程嘗試獲取某個鎖時,如果該鎖已被其他線程占用,就一直循環(huán)檢測鎖是否被釋放,而不是進(jìn)入線程掛起或睡眠狀態(tài)。

      4.6鎖消除

      消除鎖是虛擬機(jī)另外一種鎖的優(yōu)化,這種優(yōu)化更徹底,Java虛擬機(jī)在JIT編譯時(可以簡單理解為當(dāng)某段代碼即將第一次被執(zhí)行時進(jìn)行編譯,又稱即時編譯),通過對運(yùn)行上下文的掃描,去除不可能存在共享資源競爭的鎖,通過這種方式消除沒有必要的鎖,可以節(jié)省毫無意義的請求鎖時間,如下StringBuffer的append是一個同步方法,但我們將StringBuffer作為一個局部變量使用,并且不會被其他線程所使用,因此StringBuffer不可能存在共享資源競爭的情景,JVM會自動將其鎖消除。

      4.7鎖粗化

      在使用同步鎖的時候,需要讓同步塊的作用范圍盡可能小—僅在共享數(shù)據(jù)的實(shí)際作用域中才進(jìn)行同步,這樣做的目的是 為了使需要同步的操作數(shù)量盡可能縮小,如果存在鎖競爭,那么等待鎖的線程也能盡快拿到鎖。

      在大多數(shù)的情況下,上述觀點(diǎn)是正確的。但是如果一系列的連續(xù)加鎖解鎖操作,可能會導(dǎo)致不必要的性能損耗,所以引入鎖粗話的概念。

      鎖粗話概念比較好理解,就是將多個連續(xù)的加鎖、解鎖操作連接在一起,擴(kuò)展成一個范圍更大的鎖

      5、鎖升級過程

      各種鎖并不是相互代替的,而是在不同場景下的不同選擇,絕對不是說重量級鎖就是不合適的。每種鎖是只能升級,不能降級,即由偏向鎖->輕量級鎖->重量級鎖,而這個過程就是開銷逐漸加大的過程。

      如果是單線程使用,那偏向鎖毫無疑問代價最小,并且它就能解決問題,連CAS都不用做,僅僅在內(nèi)存中比較下對象頭就可以了;

      如果出現(xiàn)了其他線程競爭,則偏向鎖就會升級為輕量級鎖;

      如果其他線程通過一定次數(shù)的CAS嘗試沒有成功,則進(jìn)入重量級鎖;

      JAVA的synchronized作用是什么

      JAVA的synchronized作用是什么

      “JAVA的synchronized作用是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

      向AI問一下細(xì)節(jié)

      免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

      AI