<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          synchronized關(guān)鍵字的原理刨析

          共 3983字,需瀏覽 8分鐘

           ·

          2020-07-28 17:29

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)


          66套java從入門到精通實(shí)戰(zhàn)課程分享


          前言

          關(guān)于synchronized原理解析,我們首先要分析一下對(duì)象都有哪些東西,對(duì)象頭到底存儲(chǔ)了什么,synchronzied關(guān)鍵字到底是如何進(jìn)行鎖膨脹的,在使用過程中同步方法塊和同步代碼塊到底有什么區(qū)別,在回頭看synchronized的使用。針對(duì)的時(shí)JDK1.6之后的版本的synchronized深度分析。


          前期準(zhǔn)備

          為輸出對(duì)象頭導(dǎo)入jar


          <dependency>
          ????<groupId>org.openjdk.jolgroupId>
          ????<artifactId>jol-coreartifactId>
          ????<version>0.11version>
          dependency>


          設(shè)置偏向鎖的啟動(dòng)延遲


          #關(guān)閉偏向鎖(為什么要關(guān)閉偏向鎖,有什么好處嘛,這個(gè)問題先不做回答)
          -XX:-UseBiasedLocking
          #設(shè)置偏向鎖的一個(gè)啟動(dòng)延遲
          -XX:BiasedLockingStartupDelay=0

          對(duì)象

          對(duì)象中有哪些部分

          新建一個(gè)對(duì)象,進(jìn)行main方法輸出


          public?class?A?{
          ???int?status=0;
          ???boolean?flag=true;
          }


          public?static?void?main(String[] args) {
          ????a=new?A(); System.out.println(ClassLayout.parseInstance(a).toPrintable());
          }


          輸出的一個(gè)對(duì)象信息(當(dāng)前的操作系統(tǒng)是一個(gè)64位的,32位的是不一樣的)


          從這個(gè)上面看,我們可以分析出包含96bit(12byte*8)的對(duì)象頭和兩個(gè)屬性(status,flag)字段屬性。這些都屬于對(duì)象數(shù)據(jù),int 類型的數(shù)據(jù)我們知道,占用4個(gè)byte,1個(gè)boolean值得可以使用1個(gè)byte去表示,那么為什么后面還出現(xiàn)了7個(gè)byte,這是因?yàn)閖vm在分配內(nèi)存的時(shí)候只能是8的一個(gè)倍數(shù)。所以就出現(xiàn)了7個(gè)byte。


          所以我們小小的總結(jié)一下。


          一個(gè)對(duì)象中包含對(duì)象頭,實(shí)列數(shù)據(jù),對(duì)象填充.


          對(duì)象頭有哪些東西

          通過文檔查找來驗(yàn)證


          jvm底層一個(gè)是基于C/C++來實(shí)現(xiàn)的,查看版本java -version 可以知道jvm的實(shí)現(xiàn)是HotSport來實(shí)現(xiàn)的jvm規(guī)范和標(biāo)準(zhǔn)的。通過openjdk的官方文檔 [https://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html]查看對(duì)于對(duì)象頭的一個(gè)定義。

          說了一下包含兩部分,mark word 和 klass pointer(這個(gè)對(duì)象對(duì)應(yīng)的class類指針地址) 兩部分。在open Jdk的源碼中有一個(gè)markOop的文件中說明了在64bit的操作系統(tǒng)中,mark word 占用了64位的。所以我們得出一個(gè)結(jié)論。


          那么此時(shí)我們知道了,對(duì)象頭中包含了 這個(gè)對(duì)象對(duì)應(yīng)的class類的類型,Gc age,hashcode , synchronized關(guān)鍵字的同步狀態(tài)。


          對(duì)象頭再深入分析


          這里的klass word 為什么是32bit呢,因?yàn)樵趈vm中如果開啟了指針壓縮(1.8默認(rèn)開啟的)就會(huì)對(duì)對(duì)象頭進(jìn)行壓縮。所以看到是32bit,如果關(guān)閉就會(huì)看到是64bit。

          關(guān)閉指針壓縮


          -XX:-UseCompressedOops

          鎖的一個(gè)狀態(tài)分析

          new 對(duì)象A


          public?static?void?main(String[] args) {
          ???a=new?A();
          ???System.out.println(ClassLayout.parseInstance(a).toPrintable());
          ???a.hashCode();
          ???System.out.println(ClassLayout.parseInstance(a).toPrintable());
          }

          第一次打印這個(gè)對(duì)象A的情況(沒有計(jì)算a的hashcode)無鎖可偏向,就是沒有線程來獲取這把鎖,也沒有計(jì)算hashcode。

          全部為0的就是沒有HashCode 或者 線程ID 是空的。


          第二次打印這個(gè)對(duì)象A的情況(計(jì)算了a的hashcode),無鎖不可偏向,沒有線程來獲取這把鎖,但是計(jì)算了hashcode。


          并設(shè)置了HashCode 到mark word 中。

          當(dāng)我們的代碼變成這樣下面這樣時(shí),我們?cè)诳催@個(gè)對(duì)象頭的中mark word 的一個(gè)鎖的狀態(tài)。


          public?static?void?main(String[] args) {
          ???a=new?A();
          ???System.out.println(ClassLayout.parseInstance(a).toPrintable());
          ???a.hashCode();
          ???System.out.println(ClassLayout.parseInstance(a).toPrintable());
          ???lock();

          ???new?Thread(()->{
          ??????lock();
          ???}).start();
          }

          public??static??void??lock(){
          ???synchronized (a){
          ??????System.out.println("線程名稱=="+Thread.currentThread().getName());
          ??????System.out.println(ClassLayout.parseInstance(a).toPrintable());
          ???}
          }


          main線程來進(jìn)行加鎖操作,main線程第一次來加鎖,直接變成了輕量級(jí)鎖,這是為什么呢,不應(yīng)該是偏向鎖嘛。


          這是因?yàn)椋簩?duì)這個(gè)對(duì)象A進(jìn)行了一個(gè)hashcode計(jì)算,那么hashcode占據(jù)了56個(gè)bit,鎖的一個(gè)狀態(tài)就變成了無鎖,不可偏向狀態(tài)。沒有辦法進(jìn)行偏向,第一個(gè)線程來獲取鎖的時(shí)候就會(huì)變成輕量級(jí)鎖。


          此時(shí)我們把計(jì)算hashcode注釋掉


          public?static?void?main(String[] args) {
          ???a=new?A();
          ???System.out.println(ClassLayout.parseInstance(a).toPrintable());
          ?
          ???lock();

          ???new?Thread(()->{
          ??????lock();
          ???}).start();
          }

          public??static??void??lock(){
          ???synchronized (a){
          ??????System.out.println("線程名稱=="+Thread.currentThread().getName());
          ??????System.out.println(ClassLayout.parseInstance(a).toPrintable());
          ???}
          }
          ————————————————
          版權(quán)聲明:本文為CSDN博主「只穿T恤的程序員」的原創(chuàng)文章,遵循CC 4.0?BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。
          原文鏈接:https://blog.csdn.net/weixin_36114346/article/details/107488920


          運(yùn)行代碼得到一個(gè) main線程加鎖之后,對(duì)象頭中的鎖的一個(gè)狀態(tài)變成了無鎖 可偏向,但是多了一個(gè)線程ID。

          線程Thread-0運(yùn)行的一個(gè)情況,此時(shí)線程Thread-0變成了輕量級(jí)鎖,線程ID為thread-0,發(fā)生了一個(gè)鎖的膨脹。


          但是先需要撤銷偏向鎖,再把鎖的狀態(tài)變成輕量級(jí)鎖,而輕量級(jí)鎖是一個(gè)CAS操作。相對(duì)撤銷偏向鎖來說來消耗的性能要低的多。

          當(dāng)再執(zhí)行一個(gè)線程Thread-1時(shí),此時(shí)就變成了重量級(jí)鎖。從輕量級(jí)鎖變成了重量級(jí)鎖。

          到這里我們分析完了一個(gè)鎖的狀態(tài),包括鎖膨脹情況。


          畫圖總結(jié)鎖的一個(gè)膨脹

          沒有進(jìn)行HashCode運(yùn)算的流程


          進(jìn)行過HashCode運(yùn)算的流程


          基本使用

          synchronzied加鎖的對(duì)象為A,叫對(duì)象鎖 ,修飾的是代碼塊,進(jìn)入同步代碼塊之前要獲取鎖。


          public??static??void??lock(){
          ???synchronized (a){
          ??????System.out.println("線程名稱=="+Thread.currentThread().getName());
          ??????System.out.println(ClassLayout.parseInstance(a).toPrintable());
          ???}
          }


          synchronzied在靜態(tài)方法上加鎖,加鎖的類型是這個(gè)方法對(duì)應(yīng)的類鎖。進(jìn)入同步方法之前要獲取鎖。


          public???static????synchronized??void??lock2(){
          }


          synchronzied在實(shí)列方法上加鎖,加鎖的類型是這個(gè)方法對(duì)應(yīng)的對(duì)象實(shí)列鎖。進(jìn)入同步方法之前要當(dāng)前對(duì)象實(shí)列鎖。


          public?????synchronized??void??lock2(){
          }


          ————————————————

          版權(quán)聲明:本文為CSDN博主「只穿T恤的程序員」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。

          原文鏈接:

          https://blog.csdn.net/weixin_36114346/article/details/107488920


          ??? ?




          感謝點(diǎn)贊支持下哈?


          瀏覽 47
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本在线视频一区二区三区 | 精品网站999www | 国产婷婷激情综合 | 97人妻精品一区二区三区 | 超碰国产青娱乐 |