1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        13張圖,深入理解Synchronized

        共 21243字,需瀏覽 43分鐘

         ·

        2021-04-02 12:18

        前言

        在并發(fā)編程中Synchronized一直都是元老級(jí)的角色,Jdk 1.6以前大家都稱呼它為重量級(jí)鎖,相對(duì)于J U C包提供的Lock,它會(huì)顯得笨重,不過(guò)隨著Jdk 1.6對(duì)Synchronized進(jìn)行各種優(yōu)化后,Synchronized性能已經(jīng)非常快了。

        內(nèi)容大綱

        Synchronized使用方式

        SynchronizedJava提供的同步關(guān)鍵字,在多線程場(chǎng)景下,對(duì)共享資源代碼段進(jìn)行讀寫操作(必須包含寫操作,光讀不會(huì)有線程安全問(wèn)題,因?yàn)樽x操作天然具備線程安全特性),可能會(huì)出現(xiàn)線程安全問(wèn)題,我們可以使用Synchronized鎖定共享資源代碼段,達(dá)到互斥mutualexclusion)效果,保證線程安全。

        共享資源代碼段又稱為臨界區(qū)critical section),保證臨界區(qū)互斥,是指執(zhí)行臨界區(qū)critical section)的只能有一個(gè)線程執(zhí)行,其他線程阻塞等待,達(dá)到排隊(duì)效果。

        Synchronized的食用方式有三種

        • 修飾普通函數(shù),監(jiān)視器鎖(monitor)便是對(duì)象實(shí)例(this
        • 修飾靜態(tài)靜態(tài)函數(shù),視器鎖(monitor)便是對(duì)象的Class實(shí)例(每個(gè)對(duì)象只有一個(gè)Class實(shí)例)
        • 修飾代碼塊,監(jiān)視器鎖(monitor)是指定對(duì)象實(shí)例

        普通函數(shù)

        普通函數(shù)使用Synchronized的方式很簡(jiǎn)單,在訪問(wèn)權(quán)限修飾符函數(shù)返回類型間加上Synchronized。

        多線程場(chǎng)景下,threadthreadTwo兩個(gè)線程執(zhí)行incr函數(shù),incr函數(shù)作為共享資源代碼段被多線程讀寫操作,我們將它稱為臨界區(qū),為了保證臨界區(qū)互斥,使用Synchronized修飾incr函數(shù)即可。

        public class SyncTest {

            private int j = 0;
            
            /**
             * 自增方法
             */

            public synchronized void incr(){
                //臨界區(qū)代碼--start
                for (int i = 0; i < 10000; i++) {
                    j++;
                }
                //臨界區(qū)代碼--end
            }

            public int getJ() {
                return j;
            }
        }

        public class SyncMain {

            public static void main(String[] agrs) throws InterruptedException {
                SyncTest syncTest = new SyncTest();
                Thread thread = new Thread(() -> syncTest.incr());
                Thread threadTwo = new Thread(() -> syncTest.incr());
                thread.start();
                threadTwo.start();
                thread.join();
                threadTwo.join();
                //最終打印結(jié)果是20000,如果不使用synchronized修飾,就會(huì)導(dǎo)致線程安全問(wèn)題,輸出不確定結(jié)果
                System.out.println(syncTest.getJ());
            }

        }

        代碼十分簡(jiǎn)單,incr函數(shù)被synchronized修飾,函數(shù)邏輯是對(duì)j進(jìn)行10000次累加,兩個(gè)線程執(zhí)行incr函數(shù),最后輸出j結(jié)果。

        synchronized修飾函數(shù)我們簡(jiǎn)稱同步函數(shù),線程執(zhí)行稱同步函數(shù)前,需要先獲取監(jiān)視器鎖,簡(jiǎn)稱鎖,獲取鎖成功才能執(zhí)行同步函數(shù),同步函數(shù)執(zhí)行完后,線程會(huì)釋放鎖并通知喚醒其他線程獲取鎖,獲取鎖失敗「則阻塞并等待通知喚醒該線程重新獲取鎖」,同步函數(shù)會(huì)以this作為鎖,即當(dāng)前對(duì)象,以上面的代碼段為例就是syncTest對(duì)象。

        • 線程thread執(zhí)行syncTest.incr()
        • 線程thread獲取鎖成功
        • 線程threadTwo執(zhí)行syncTest.incr()
        • 線程threadTwo獲取鎖失敗
        • 線程threadTwo阻塞并等待喚醒
        • 線程thread執(zhí)行完syncTest.incr()j累積到10000
        • 線程thread釋放鎖,通知喚醒threadTwo線程獲取鎖
        • 線程threadTwo獲取鎖成功
        • 線程threadTwo執(zhí)行完syncTest.incr(),j累積到20000
        • 線程threadTwo釋放鎖

        靜態(tài)函數(shù)

        靜態(tài)函數(shù)顧名思義,就是靜態(tài)的函數(shù),它使用Synchronized的方式與普通函數(shù)一致,唯一的區(qū)別是鎖的對(duì)象不再是this,而是Class對(duì)象。

        多線程執(zhí)行Synchronized修飾靜態(tài)函數(shù)代碼段如下。

        public class SyncTest {

            private static int j = 0;
            
            /**
             * 自增方法
             */

            public static synchronized void incr(){
                //臨界區(qū)代碼--start
                for (int i = 0; i < 10000; i++) {
                    j++;
                }
                //臨界區(qū)代碼--end
            }

            public static int getJ() {
                return j;
            }
        }

        public class SyncMain {

            public static void main(String[] agrs) throws InterruptedException {
                Thread thread = new Thread(() -> SyncTest.incr());
                Thread threadTwo = new Thread(() -> SyncTest.incr());
                thread.start();
                threadTwo.start();
                thread.join();
                threadTwo.join();
                //最終打印結(jié)果是20000,如果不使用synchronized修飾,就會(huì)導(dǎo)致線程安全問(wèn)題,輸出不確定結(jié)果
                System.out.println(SyncTest.getJ());
            }

        }

        Java的靜態(tài)資源可以直接通過(guò)類名調(diào)用,靜態(tài)資源不屬于任何實(shí)例對(duì)象,它只屬于Class對(duì)象,每個(gè)ClassJ V M中只有唯一的一個(gè)Class對(duì)象,所以同步靜態(tài)函數(shù)會(huì)以Class對(duì)象作為鎖,后續(xù)獲取鎖、釋放鎖流程都一致。

        代碼塊

        前面介紹的普通函數(shù)與靜態(tài)函數(shù)粒度都比較大,以整個(gè)函數(shù)為范圍鎖定,現(xiàn)在想把范圍縮小、靈活配置,就需要使用代碼塊了,使用{}符號(hào)定義范圍給Synchronized修飾。

        下面代碼中定義了syncDbData函數(shù),syncDbData是一個(gè)偽同步數(shù)據(jù)的函數(shù),耗時(shí)2秒,并且邏輯不涉及共享資源讀寫操作非臨界區(qū)),另外還有兩個(gè)函數(shù)incrincrTwo,都是在自增邏輯前執(zhí)行了syncDbData函數(shù),只是使用Synchronized的姿勢(shì)不同,一個(gè)是修飾在函數(shù)上,另一個(gè)是修飾在代碼塊上。

        public class SyncTest {

            private static int j = 0;


            /**
             * 同步庫(kù)數(shù)據(jù),比較耗時(shí),代碼資源不涉及共享資源讀寫操作。
             */

            public void syncDbData() {
                System.out.println("db數(shù)據(jù)開始同步------------");
                try {
                    //同步時(shí)間需要2秒
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("db數(shù)據(jù)開始同步完成------------");
            }

            //自增方法
            public synchronized void incr() {
                //start--臨界區(qū)代碼
                //同步庫(kù)數(shù)據(jù)
                syncDbData();
                for (int i = 0; i < 10000; i++) {
                    j++;
                }
                //end--臨界區(qū)代碼
            }

            //自增方法
            public void incrTwo() {
                //同步庫(kù)數(shù)據(jù)
                syncDbData();
                synchronized (this) {
                    //start--臨界區(qū)代碼
                    for (int i = 0; i < 10000; i++) {
                        j++;
                    }
                    //end--臨界區(qū)代碼
                }

            }

            public int getJ() {
                return j;
            }

        }


        public class SyncMain {

            public static void main(String[] agrs) throws InterruptedException {
                //incr同步方法執(zhí)行
                SyncTest syncTest = new SyncTest();
                Thread thread = new Thread(() -> syncTest.incr());
                Thread threadTwo = new Thread(() -> syncTest.incr());
                thread.start();
                threadTwo.start();
                thread.join();
                threadTwo.join();
                //最終打印結(jié)果是20000
                System.out.println(syncTest.getJ());

                //incrTwo同步塊執(zhí)行
                thread = new Thread(() -> syncTest.incrTwo());
                threadTwo = new Thread(() -> syncTest.incrTwo());
                thread.start();
                threadTwo.start();
                thread.join();
                threadTwo.join();
                //最終打印結(jié)果是40000
                System.out.println(syncTest.getJ());
            }

        }

        先看看incr同步方法執(zhí)行,流程和前面沒(méi)區(qū)別,只是Synchronized鎖定的范圍太大,把syncDbData()也納入臨界區(qū)中,多線程場(chǎng)景執(zhí)行,會(huì)有性能上的浪費(fèi),因?yàn)?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">syncDbData()完全可以讓多線程并行并發(fā)執(zhí)行。

        我們通過(guò)代碼塊的方式,來(lái)縮小范圍,定義正確的臨界區(qū),提升性能,目光轉(zhuǎn)到incrTwo同步塊執(zhí)行,incrTwo函數(shù)使用修飾代碼塊的方式同步,只對(duì)自增代碼段進(jìn)行鎖定。

        代碼塊同步方式除了靈活控制范圍外,還能做線程間的協(xié)同工作,因?yàn)?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">Synchronized ()括號(hào)中能接收任何對(duì)象作為鎖,所以可以通過(guò)Objectwait、notify、notifyAll等函數(shù),做多線程間的通信協(xié)同(本文不對(duì)線程通信協(xié)同做展開,主角是Synchronized,而且也不推薦去用這些方法,因?yàn)?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">LockSupport工具類會(huì)是更好的選擇)。

        • wait:當(dāng)前線程暫停,釋放鎖
        • notify:釋放鎖,喚醒調(diào)用了wait的線程(如果有多個(gè)隨機(jī)喚醒一個(gè))
        • notifyAll:釋放鎖,喚醒調(diào)用了wait的所有線程

        Synchronized原理

          public class SyncTest {

            private static int j = 0;


            /**
             * 同步庫(kù)數(shù)據(jù),比較耗時(shí),代碼資源不涉及共享資源讀寫操作。
             */

            public void syncDbData() {
                System.out.println("db數(shù)據(jù)開始同步------------");
                try {
                    //同步時(shí)間需要2秒
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("db數(shù)據(jù)開始同步完成------------");
            }

            //自增方法
            public synchronized void incr() {
                //start--臨界區(qū)代碼
                //同步庫(kù)數(shù)據(jù)
                syncDbData();
                for (int i = 0; i < 10000; i++) {
                    j++;
                }
                //end--臨界區(qū)代碼
            }

            //自增方法
            public void incrTwo() {
                //同步庫(kù)數(shù)據(jù)
                syncDbData();
                synchronized (this) {
                    //start--臨界區(qū)代碼
                    for (int i = 0; i < 10000; i++) {
                        j++;
                    }
                    //end--臨界區(qū)代碼
                }

            }

            public int getJ() {
                return j;
            }


        為了探究Synchronized原理,我們對(duì)上面的代碼進(jìn)行反編譯,輸出反編譯后結(jié)果,看看底層是如何實(shí)現(xiàn)的(環(huán)境Java 11、win 10系統(tǒng))。

          只截取了incr與incrTwo函數(shù)內(nèi)容
                
          public synchronized void incr();
            Code:
               0: aload_0                                         
               1: invokevirtual #11                 // Method syncDbData:()V 
               4: iconst_0                          
               5: istore_1                          
               6: iload_1                                     
               7: sipush        10000               
              10: if_icmpge     27
              13: getstatic     #12                 // Field j:I
              16: iconst_1
              17: iadd
              18: putstatic     #12                 // Field j:I
              21: iinc          11
              24: goto          6
              27return

          public void incrTwo();    
            Code:
               0: aload_0
               1: invokevirtual #11                 // Method syncDbData:()V
               4: aload_0
               5: dup
               6: astore_1
               7: monitorenter                     //獲取鎖
               8: iconst_0
               9: istore_2
              10: iload_2
              11: sipush        10000
              14: if_icmpge     31
              17: getstatic     #12                 // Field j:I
              20: iconst_1
              21: iadd
              22: putstatic     #12                 // Field j:I
              25: iinc          21
              28: goto          10
              31: aload_1
              32: monitorexit                      //正常退出釋放鎖 
              33: goto          41
              36: astore_3
              37: aload_1
              38: monitorexit                      //異步退出釋放鎖    
              39: aload_3
              40: athrow
              41return

        ps:對(duì)上面指令感興趣的讀者,可以百度或google一下“JVM 虛擬機(jī)字節(jié)碼指令表”

        先看incrTwo函數(shù),incrTwo是代碼塊方式同步,在反編譯后的結(jié)果中,我們發(fā)現(xiàn)存在monitorentermonitorexit指令(獲取鎖、釋放鎖)。

        monitorenter指令插入到同步代碼塊的開始位置,monitorexit指令插入到同步代碼塊的結(jié)束位置,J V M需要保證每一個(gè) monitorenter都有monitorexit與之對(duì)應(yīng)。

        任何對(duì)象都有一個(gè)監(jiān)視器鎖(monitor)關(guān)聯(lián),線程執(zhí)行monitorenter指令時(shí)嘗試獲取monitor的所有權(quán)。

        • 如果monitor的進(jìn)入數(shù)為0,則該線程進(jìn)入monitor,然后將進(jìn)入數(shù)設(shè)置為1,該線程為monitor的所有者
        • 如果線程已經(jīng)占有該monitor,重新進(jìn)入,則monitor的進(jìn)入數(shù)加1
        • 線程執(zhí)行monitorexit,monitor的進(jìn)入數(shù)-1,執(zhí)行過(guò)多少次monitorenter,最終要執(zhí)行對(duì)應(yīng)次數(shù)的monitorexit
        • 如果其他線程已經(jīng)占用monitor,則該線程進(jìn)入阻塞狀態(tài),直到monitor的進(jìn)入數(shù)為0,再重新嘗試獲取monitor的所有權(quán)

        回過(guò)頭看incr函數(shù),incr是普通函數(shù)方式同步,雖然在反編譯后的結(jié)果中沒(méi)有看到monitorentermonitorexit指令,但是實(shí)際執(zhí)行的流程與incrTwo函數(shù)一樣,通過(guò)monitor來(lái)執(zhí)行,只不過(guò)它是一種隱式的方式來(lái)實(shí)現(xiàn),最后放一張流程圖。

        Synchronized優(yōu)化

        Jdk 1.5以后對(duì)Synchronized關(guān)鍵字做了各種的優(yōu)化,經(jīng)過(guò)優(yōu)化后Synchronized已經(jīng)變得原來(lái)越快了,這也是為什么官方建議使用Synchronized的原因,具體的優(yōu)化點(diǎn)如下。

        • 鎖粗化
        • 鎖消除
        • 鎖升級(jí)

        鎖粗化

        互斥的臨界區(qū)范圍應(yīng)該盡可能小,這樣做的目的是為了使同步的操作數(shù)量盡可能縮小,縮短阻塞時(shí)間,如果存在鎖競(jìng)爭(zhēng),那么等待鎖的線程也能盡快拿到鎖。

        但是加鎖解鎖也需要消耗資源,如果存在一系列的連續(xù)加鎖解鎖操作,可能會(huì)導(dǎo)致不必要的性能損耗,鎖粗化就是將「多個(gè)連續(xù)的加鎖、解鎖操作連接在一起」,擴(kuò)展成一個(gè)范圍更大的鎖,避免頻繁的加鎖解鎖操作。

        J V M會(huì)檢測(cè)到一連串的操作都對(duì)同一個(gè)對(duì)象加鎖(for循環(huán)10000次執(zhí)行j++,沒(méi)有鎖粗化就要進(jìn)行10000次加鎖/解鎖),此時(shí)J V M就會(huì)將加鎖的范圍粗化到這一連串操作的外部(比如for循環(huán)體外),使得這一連串操作只需要加一次鎖即可。

        鎖消除

        Java虛擬機(jī)在JIT編譯時(shí)(可以簡(jiǎn)單理解為當(dāng)某段代碼即將第一次被執(zhí)行時(shí)進(jìn)行編譯,又稱即時(shí)編譯),通過(guò)對(duì)運(yùn)行上下文的掃描,經(jīng)過(guò)逃逸分析(對(duì)象在函數(shù)中被使用,也可能被外部函數(shù)所引用,稱為函數(shù)逃逸),去除不可能存在共享資源競(jìng)爭(zhēng)的鎖,通過(guò)這種方式消除沒(méi)有必要的鎖,可以節(jié)省毫無(wú)意義的時(shí)間消耗。

        代碼中使用Object作為鎖,但是Object對(duì)象的生命周期只在incrFour()函數(shù)中,并不會(huì)被其他線程所訪問(wèn)到,所以在J I T編譯階段就會(huì)被優(yōu)化掉(此處的Object屬于沒(méi)有逃逸的對(duì)象)。

        鎖升級(jí)

        Java中每個(gè)對(duì)象都擁有對(duì)象頭,對(duì)象頭由Mark World 、指向類的指針、以及數(shù)組長(zhǎng)度三部分組成,本文,我們只需要關(guān)心Mark World 即可,Mark World  記錄了對(duì)象的HashCode、分代年齡和鎖標(biāo)志位信息。

        Mark World簡(jiǎn)化結(jié)構(gòu)

        鎖狀態(tài)存儲(chǔ)內(nèi)容鎖標(biāo)記
        無(wú)鎖對(duì)象的hashCode、對(duì)象分代年齡、是否是偏向鎖(0)01
        偏向鎖偏向線程ID、偏向時(shí)間戳、對(duì)象分代年齡、是否是偏向鎖(1)01
        輕量級(jí)鎖指向棧中鎖記錄的指針00
        重量級(jí)鎖指向互斥量(重量級(jí)鎖)的指針10

        讀者們只需知道,鎖的升級(jí)變化,體現(xiàn)在鎖對(duì)象的對(duì)象頭Mark World部分,也就是說(shuō)Mark World的內(nèi)容會(huì)隨著鎖升級(jí)而改變。

        Java1.5以后為了減少獲取鎖和釋放鎖帶來(lái)的性能消耗,引入了偏向鎖輕量級(jí)鎖Synchronized的升級(jí)順序是 「無(wú)鎖-->偏向鎖-->輕量級(jí)鎖-->重量級(jí)鎖,只會(huì)升級(jí)不會(huì)降級(jí)

        偏向鎖

        在大多數(shù)情況下,鎖總是由同一線程多次獲得,不存在多線程競(jìng)爭(zhēng),所以出現(xiàn)了偏向鎖,其目標(biāo)就是在只有一個(gè)線程執(zhí)行同步代碼塊時(shí),降低獲取鎖帶來(lái)的消耗,提高性能(可以通過(guò)J V M參數(shù)關(guān)閉偏向鎖:-XX:-UseBiasedLocking=false,關(guān)閉之后程序默認(rèn)會(huì)進(jìn)入輕量級(jí)鎖狀態(tài))。

        線程執(zhí)行同步代碼或方法前,線程只需要判斷對(duì)象頭的Mark Word中線程ID與當(dāng)前線程ID是否一致,如果一致直接執(zhí)行同步代碼或方法,具體流程如下

        • 無(wú)鎖狀態(tài),存儲(chǔ)內(nèi)容「是否為偏向鎖(0)」,鎖標(biāo)識(shí)位01
          • CAS設(shè)置當(dāng)前線程ID到Mark Word存儲(chǔ)內(nèi)容中
          • 是否為偏向鎖0 => 是否為偏向鎖1
          • 執(zhí)行同步代碼或方法
        • 偏向鎖狀態(tài),存儲(chǔ)內(nèi)容「是否為偏向鎖(1)、線程ID」,鎖標(biāo)識(shí)位01
          • 對(duì)比線程ID是否一致,如果一致執(zhí)行同步代碼或方法,否則進(jìn)入下面的流程
          • 如果不一致,CASMark Word的線程ID設(shè)置為當(dāng)前線程ID,設(shè)置成功,執(zhí)行同步代碼或方法,否則進(jìn)入下面的流程
          • CAS設(shè)置失敗,證明存在多線程競(jìng)爭(zhēng)情況,觸發(fā)撤銷偏向鎖,當(dāng)?shù)竭_(dá)全局安全點(diǎn),偏向鎖的線程被掛起,偏向鎖升級(jí)為輕量級(jí)鎖,然后在安全點(diǎn)的位置恢復(fù)繼續(xù)往下執(zhí)行。

        輕量級(jí)鎖

        輕量級(jí)鎖考慮的是競(jìng)爭(zhēng)鎖對(duì)象的線程不多,持有鎖時(shí)間也不長(zhǎng)的場(chǎng)景。因?yàn)樽枞€程需要C P U從用戶態(tài)轉(zhuǎn)到內(nèi)核態(tài),代價(jià)較大,如果剛剛阻塞不久這個(gè)鎖就被釋放了,那這個(gè)代價(jià)就有點(diǎn)得不償失,所以干脆不阻塞這個(gè)線程,讓它自旋一段時(shí)間等待鎖釋放。

        當(dāng)前線程持有的鎖是偏向鎖的時(shí)候,被另外的線程所訪問(wèn),偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖,其他線程會(huì)通過(guò)自旋的形式嘗試獲取鎖,不會(huì)阻塞,從而提高性能。輕量級(jí)鎖的獲取主要有兩種情況:① 當(dāng)關(guān)閉偏向鎖功能時(shí);② 多個(gè)線程競(jìng)爭(zhēng)偏向鎖導(dǎo)致偏向鎖升級(jí)為輕量級(jí)鎖。

        • 無(wú)鎖狀態(tài),存儲(chǔ)內(nèi)容「是否為偏向鎖(0)」,鎖標(biāo)識(shí)位01
          • 關(guān)閉偏向鎖功能時(shí)
          • CAS設(shè)置當(dāng)前線程棧中鎖記錄的指針到Mark Word存儲(chǔ)內(nèi)容
          • 鎖標(biāo)識(shí)位設(shè)置為00
          • 執(zhí)行同步代碼或方法
          • 釋放鎖時(shí),還原來(lái)Mark Word內(nèi)容
        • 輕量級(jí)鎖狀態(tài),存儲(chǔ)內(nèi)容「線程棧中鎖記錄的指針」,鎖標(biāo)識(shí)位00(存儲(chǔ)內(nèi)容的線程是指"持有輕量級(jí)鎖的線程")
          • CAS設(shè)置當(dāng)前線程棧中鎖記錄的指針到Mark Word存儲(chǔ)內(nèi)容,設(shè)置成功獲取輕量級(jí)鎖,執(zhí)行同步塊代碼或方法,否則執(zhí)行下面的邏輯
          • 設(shè)置失敗,證明多線程存在一定競(jìng)爭(zhēng),線程自旋上一步的操作,自旋一定次數(shù)后還是失敗,輕量級(jí)鎖升級(jí)為重量級(jí)鎖
          • Mark Word存儲(chǔ)內(nèi)容替換成重量級(jí)鎖指針,鎖標(biāo)記位10

        重量級(jí)鎖

        輕量級(jí)鎖膨脹之后,就升級(jí)為重量級(jí)鎖,重量級(jí)鎖是依賴操作系統(tǒng)的MutexLock互斥鎖)來(lái)實(shí)現(xiàn)的,需要從用戶態(tài)轉(zhuǎn)到內(nèi)核態(tài),這個(gè)成本非常高,這就是為什么Java1.6之前Synchronized效率低的原因。

        升級(jí)為重量級(jí)鎖時(shí),鎖標(biāo)志位的狀態(tài)值變?yōu)?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">10,此時(shí)Mark Word中存儲(chǔ)內(nèi)容的是重量級(jí)鎖的指針,等待鎖的線程都會(huì)進(jìn)入阻塞狀態(tài),下面是簡(jiǎn)化版的鎖升級(jí)過(guò)程。

        1. Spring Boot 搭建 ELK,這才是正確看日志的方式!

        2. 如何甄別應(yīng)聘者簡(jiǎn)歷的包裝程度?

        3. Nacos 2.0 正式發(fā)布,性能大幅提升 10 倍!

        4. Redis單線程已經(jīng)很快了,為什么6.0要引入多線程?帶來(lái)什么優(yōu)勢(shì)?

        最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊(cè),覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。

        獲取方式:點(diǎn)“在看”,關(guān)注公眾號(hào)并回復(fù) Java 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

        文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

        謝謝支持喲 (*^__^*)

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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            狂野欧美性猛交XXⅩ大乱3超A | 成人片黄网站色大片免费无码 | 青娱乐人体艺术 | 慈禧一级淫片免费观看 | 30岁的寡妇两个大乳子 | 黄色短视频免费 | 自拍偷拍视频网 | 嗯~用力啊~嗯~c我~视频 | 男人天堂a| cao到她乖1v1高h黑暗森林 |