<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                http://www.infoq.com/cn/articles/java-se-16-synchronized [TOC] ## 1 引言 在多線程并發編程中Synchronized一直是元老級角色,很多人都會稱呼它為重量級鎖,但是隨著Java SE1.6對Synchronized進行了各種優化之后,有些情況下它并不那么重了,本文詳細介紹了Java SE1.6中為了減少獲得鎖和釋放鎖帶來的性能消耗而引入的偏向鎖和輕量級鎖,以及鎖的存儲結構和升級過程。 ## 2 術語定義 | | | | | --- | --- | --- | | 術語 | 英文| 說明 | | CAS | Compare and Swap | 比較并設置。用于在硬件層面上提供原子性操作。在?Intel?處理器中,比較并交換通過指令cmpxchg實現。比較是否和給定的數值一致,如果一致則修改,不一致則不修改。 | ## 3 同步的基礎 Java中的每一個對象都可以作為鎖。 * 對于同步方法,鎖是當前實例對象。 * 對于靜態同步方法,鎖是當前對象的Class對象。 * 對于同步方法塊,鎖是Synchonized括號里配置的對象。 當一個線程試圖訪問同步代碼塊時,它首先必須得到鎖,退出或拋出異常時必須釋放鎖。那么鎖存在哪里呢?鎖里面會存儲什么信息呢? ## 4 同步的原理 JVM規范規定JVM基于進入和退出Monitor對象來實現方法同步和代碼塊同步,但兩者的實現細節不一樣。代碼塊同步是使用monitorenter和monitorexit指令實現,而方法同步是使用另外一種方式實現的,細節在JVM規范里并沒有詳細說明,但是方法的同步同樣可以使用這兩個指令來實現。monitorenter指令是在編譯后插入到同步代碼塊的開始位置,而monitorexit是插入到方法結束處和異常處, JVM要保證每個monitorenter必須有對應的monitorexit與之配對。任何對象都有一個 monitor 與之關聯,當且一個monitor 被持有后,它將處于鎖定狀態。線程執行到 monitorenter 指令時,將會嘗試獲取對象所對應的 monitor 的所有權,即嘗試獲得對象的鎖。 ### 4.1 Java對象頭 鎖存在Java對象頭里。如果對象是數組類型,則虛擬機用3個Word(字寬)存儲對象頭,如果對象是非數組類型,則用2字寬存儲對象頭。在32位虛擬機中,一字寬等于四字節,即32bit。 | 長度 | 內容 | 說明 | | 32/64bit | Mark Word | 存儲對象的hashCode或鎖信息等。 | | 32/64bit | Class Metadata Address | 存儲到對象類型數據的指針 | | 32/64bit | Array length | 數組的長度(如果當前對象是數組) | Java對象頭里的Mark Word里默認存儲對象的HashCode,分代年齡和鎖標記位。32位JVM的Mark Word的默認存儲結構如下: | ? | 25 bit | 4bit | 1bit 是否是偏向鎖 | 2bit 鎖標志位 | | 無鎖狀態 | 對象的hashCode | 對象分代年齡 | 0 | 01 | 在運行期間Mark Word里存儲的數據會隨著鎖標志位的變化而變化。Mark Word可能變化為存儲以下4種數據: | 鎖狀態 | 25 bit | 4bit | 1bit | 2bit | | 23bit | 2bit | 是否是偏向鎖 | 鎖標志位 | | 輕量級鎖 | 指向棧中鎖記錄的指針 | 00 | | 重量級鎖 | 指向互斥量(重量級鎖)的指針 | 10 | | GC標記 | 空 | 11 | | 偏向鎖 | 線程ID | Epoch | 對象分代年齡 | 1 | 01 | 在64位虛擬機下,Mark Word是64bit大小的,其存儲結構如下:? | 鎖狀態 | 25bit | 31bit | 1bit | 4bit | 1bit | 2bit | | ? | ? | cms_free | 分代年齡 | 偏向鎖 | 鎖標志位 | | 無鎖 | unused | hashCode | ? | ? | 0 | 01 | | 偏向鎖 | ThreadID(54bit) Epoch(2bit) | ? | ? | 1 | 01 | ### 4.2 鎖的升級 Java SE1.6為了減少獲得鎖和釋放鎖所帶來的性能消耗,引入了“偏向鎖”和“輕量級鎖”,所以在Java SE1.6里鎖一共有四種狀態,無鎖狀態,偏向鎖狀態,輕量級鎖狀態和重量級鎖狀態,它會隨著競爭情況逐漸升級。鎖可以升級但不能降級,意味著偏向鎖升級成輕量級鎖后不能降級成偏向鎖。這種鎖升級卻不能降級的策略,目的是為了提高獲得鎖和釋放鎖的效率,下文會詳細分析。 ![](https://res.infoq.com/articles/java-se-16-synchronized/zh/resources/image1.png) ### 4.3 偏向鎖 Hotspot的作者經過以往的研究發現大多數情況下鎖不僅不存在多線程競爭,而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價更低而引入了偏向鎖。當一個線程訪問同步塊并獲取鎖時,會在對象頭和棧幀中的鎖記錄里存儲鎖偏向的線程ID,以后該線程在進入和退出同步塊時不需要花費CAS操作來加鎖和解鎖,而只需簡單的測試一下對象頭的Mark Word里是否存儲著指向當前線程的偏向鎖,如果測試成功,表示線程已經獲得了鎖,如果測試失敗,則需要再測試下Mark Word中偏向鎖的標識是否設置成1(表示當前是偏向鎖),如果沒有設置,則使用CAS競爭鎖,如果設置了,則嘗試使用CAS將對象頭的偏向鎖指向當前線程。 偏向鎖的撤銷:偏向鎖使用了一種等到競爭出現才釋放鎖的機制,所以當其他線程嘗試競爭偏向鎖時,持有偏向鎖的線程才會釋放鎖。偏向鎖的撤銷,需要等待全局安全點(在這個時間點上沒有字節碼正在執行),它會首先暫停擁有偏向鎖的線程,然后檢查持有偏向鎖的線程是否活著,如果線程不處于活動狀態,則將對象頭設置成無鎖狀態,如果線程仍然活著,擁有偏向鎖的棧會被執行,遍歷偏向對象的鎖記錄,棧中的鎖記錄和對象頭的Mark Word要么重新偏向于其他線程,要么恢復到無鎖或者標記對象不適合作為偏向鎖,最后喚醒暫停的線程。下圖中的線程1演示了偏向鎖初始化的流程,線程2演示了偏向鎖撤銷的流程。 ![](https://res.infoq.com/articles/java-se-16-synchronized/zh/resources/image2.png) 關閉偏向鎖:偏向鎖在Java 6和Java 7里是默認啟用的,但是它在應用程序啟動幾秒鐘之后才激活,如有必要可以使用JVM參數來關閉延遲-XX:BiasedLockingStartupDelay = 0。如果你確定自己應用程序里所有的鎖通常情況下處于競爭狀態,可以通過JVM參數關閉偏向鎖-XX:-UseBiasedLocking=false,那么默認會進入輕量級鎖狀態。 ### 4.4 輕量級鎖 輕量級鎖加鎖:線程在執行同步塊之前,JVM會先在當前線程的棧楨中創建用于存儲鎖記錄的空間,并將對象頭中的Mark Word復制到鎖記錄中,官方稱為Displaced Mark Word。然后線程嘗試使用CAS將對象頭中的Mark Word替換為指向鎖記錄的指針。如果成功,當前線程獲得鎖,如果失敗,表示其他線程競爭鎖,當前線程便嘗試使用自旋來獲取鎖。 輕量級鎖解鎖:輕量級解鎖時,會使用原子的CAS操作來將Displaced Mark Word替換回到對象頭,如果成功,則表示沒有競爭發生。如果失敗,表示當前鎖存在競爭,鎖就會膨脹成重量級鎖。下圖是兩個線程同時爭奪鎖,導致鎖膨脹的流程圖。 ![](https://res.infoq.com/articles/java-se-16-synchronized/zh/resources/image3.png) 因為自旋會消耗CPU,為了避免無用的自旋(比如獲得鎖的線程被阻塞住了),一旦鎖升級成重量級鎖,就不會再恢復到輕量級鎖狀態。當鎖處于這個狀態下,其他線程試圖獲取鎖時,都會被阻塞住,當持有鎖的線程釋放鎖之后會喚醒這些線程,被喚醒的線程就會進行新一輪的奪鎖之爭。 ## 5 鎖的優缺點對比 | 鎖 | 優點 | 缺點 | 適用場景 | | 偏向鎖 | 加鎖和解鎖不需要額外的消耗,和執行非同步方法比僅存在納秒級的差距。 | 如果線程間存在鎖競爭,會帶來額外的鎖撤銷的消耗。 | 適用于只有一個線程訪問同步塊場景。 | | 輕量級鎖 | 競爭的線程不會阻塞,提高了程序的響應速度。 | 如果始終得不到鎖競爭的線程使用自旋會消耗CPU。 | 追求響應時間。 同步塊執行速度非常快。 | | 重量級鎖 | 線程競爭不使用自旋,不會消耗CPU。 | 線程阻塞,響應時間緩慢。 | 追求吞吐量。 同步塊執行速度較長。 | ## 6 參考源碼 本文一些內容參考了[HotSpot](http://hg.openjdk.java.net/hsx/hotspot-main/hotspot/file/61b82be3b1ff/)源碼 。對象頭源碼markOop.hpp。偏向鎖源碼biasedLocking.cpp。以及其他源碼ObjectMonitor.cpp和BasicLock.cpp。 ## 7 參考資料 * [偏向鎖](http://www.oracle.com/technetwork/java/javase/tech/biasedlocking-oopsla2006-preso-150106.pdf) * [java-overview-and-java-se6](http://pdffinder.net/Java-Overview-and-Java-SE-6-What's-New.html)?Synchronization Optimization章節 * Dave Dice?[“Synchronization in Java SE 6”](http://home.comcast.net/~pjbishop/Dave/MustangSync.pdf) * [Java SE 6 Performance White Paper](http://java.sun.com/performance/reference/whitepapers/6_performance.html#2.1.3)?2.1章節 * [JVM規范(Java SE 7)](http://docs.oracle.com/javase/specs/jvms/se7/html/index.html) * [Java語言規范(JAVA SE7)](http://docs.oracle.com/javase/specs/jls/se7/html/) * [周志明的《深入理解Java虛擬機》](http://book.douban.com/subject/6522893/) * [Java偏向鎖實現原理](http://kenwublog.com/theory-of-java-biased-locking) ## 作者簡介 方騰飛,阿里巴巴資深軟件開發工程師,致力于高性能網絡和并發編程,目前在公司從事詢盤管理和長連接服務器OpenComet的開發工作。 博客地址:[http://ifeve.com](http://ifeve.com/)?微博地址:[http://weibo.com/kirals](http://weibo.com/kirals) * * * 感謝[張龍](http://www.infoq.com/cn/bycategory.action?authorName=%E5%BC%A0%E9%BE%99)對本文的審校。 給InfoQ中文站投稿或者參與內容翻譯工作,請郵件至[editors@cn.infoq.com](mailto:editors@cn.infoq.com)。也歡迎大家通過新浪微博([@InfoQ](http://www.weibo.com/infoqchina))或者騰訊微博([@InfoQ](http://t.qq.com/infoqchina))關注我們,并與我們的編輯和其他讀者朋友交流。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看