免费爱碰视频在线观看,九九精品国产屋,欧美亚洲尤物久久精品,1024在线观看视频亚洲

      面試官:有了解過Synchronized嗎 說說看

      面試官: 有了解過Synchronized嗎 說說看

      前言

      目前正在出一個(gè)Java多線程專題長期系列教程,從入門到進(jìn)階源碼解讀, 篇幅會(huì)較多, 喜歡的話,給個(gè)關(guān)注 ~ 本篇內(nèi)容純理論一點(diǎn)

      相信很多同學(xué)對synchronized的使用上不陌生,之前也給大家講解過它的使用。本篇主要帶大家深入了解一下它,大家也可以自己試著總結(jié)一下,這也是面試中常常問到的,單純的回答它的基本使用,是驚艷不到面試官的~

      synchronized 介紹

      從字面意思翻譯過來就是同步的意思,所以它也叫同步鎖,我們通常會(huì)給某個(gè)方法或者某塊代碼加上Synchronized鎖來解決多線程中并發(fā)帶來的問題,它也是最常用,最簡單的一種方法

      在Java中,鎖基本上都是基于對象而言的,所以又稱為對象鎖, 一個(gè)類通常只有一個(gè)class對象和n個(gè)實(shí)例對象,它們共享class對象,而我們有時(shí)候會(huì)對class對象加鎖,所以又稱為class對象鎖

      這里大家要注意的是對象需要是一個(gè)非null的對象,我們通常也叫做對象監(jiān)視器(Object Monitor)

      重量級(jí)鎖

      在JDK 1.5之前,它是一個(gè)重量級(jí)鎖,我們通常都會(huì)使用它來保證線程同步。在1.5的時(shí)候還提供了一個(gè)Lock接口來實(shí)現(xiàn)同步鎖的功能,我們只需要顯式的獲取鎖和釋放鎖。

      重在哪

      在1.5的時(shí)候,Synchronized它依賴于操作系統(tǒng)底層的Mutex Lock實(shí)現(xiàn),每次釋放鎖和獲取鎖都會(huì)導(dǎo)致用戶態(tài)和內(nèi)核態(tài)的切換,從而增加系統(tǒng)性能的開銷,當(dāng)出現(xiàn)大并發(fā)的情況下,鎖競爭會(huì)比較激烈,性能顯得非常糟糕,所以稱為重量級(jí)鎖,所以大家往往會(huì)選擇Lock鎖。

      鎖優(yōu)化

      但是Synchronized又是那么的簡單好用,又是官方自帶的,怎么可能放棄呢?所以在1.6之后,引入了大量的鎖優(yōu)化,比如自旋鎖,輕量級(jí)鎖, 偏向鎖等,下面我們逐個(gè)看一下~

      synchronized 實(shí)現(xiàn)原理

      我們了解鎖優(yōu)化之前,我們先看一下它的實(shí)現(xiàn)原理。

      首先我們看下同步塊中,因?yàn)樗顷P(guān)鍵字,我們看不到源碼實(shí)現(xiàn),所以只能反編譯看一下,通過 javap -v **.class

      public static void main(String[] args) { synchronized(Demo.class) { System.out.println(“hello”); } }public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: ldc #2 // class com/thread/base/Demo 2: dup 3: astore_1 4: monitorenter 5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 8: ldc #4 // String hello 10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 13: aload_1 14: monitorexit 15: goto 23 18: astore_2 19: aload_1 20: monitorexit 21: aload_2 22: athrow 23: return

      我們重點(diǎn)關(guān)注monitorenter和monitorexit,那么他倆是什么意思呢

      monitorenter,如果當(dāng)前 monitor 的進(jìn)入數(shù)為 0 時(shí),線程就會(huì)進(jìn)入 monitor,并且把進(jìn)入數(shù) + 1,那么該線程就是 monitor 的擁有者 (owner)。如果該線程已經(jīng)是 monitor 的擁有者,又重新進(jìn)入,就會(huì)把進(jìn)入數(shù)再次 + 1。也就是可重入。

      monitorexit,執(zhí)行 monitorexit 的線程必須是 monitor 的擁有者,指令執(zhí)行后,monitor 的進(jìn)入數(shù)減 1,如果減 1 后進(jìn)入數(shù)為 0,則該線程會(huì)退出 monitor。其他被阻塞的線程就可以嘗試去獲取 monitor 的所有權(quán)。指令出現(xiàn)了兩次,第 1 次為同步正常退出釋放鎖;第2次為發(fā)生異步退出釋放鎖;

      我們再來看一下, 修飾實(shí)例方法中的表現(xiàn):

      class Demo { public synchronized void hello() { System.out.println(“hello”); }} public synchronized void hello(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String hello 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 25: 0 line 26: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this Lcom/thread/base/Demo;}

      我們重點(diǎn)關(guān)注ACC_SYNCHRONIZED,它作用就是一旦執(zhí)行到這個(gè)方法時(shí),就會(huì)先判斷是否有標(biāo)志位,如果有,就會(huì)先嘗試獲取 monitor,獲取成功才能執(zhí)行方法,方法執(zhí)行完成后再釋放 monitor。在方法執(zhí)行期間,其他線程都無法獲取同一個(gè) monitor。歸根結(jié)底還是對 monitor 對象的爭奪,只是同步方法是一種隱式的方式來實(shí)現(xiàn)。

      synchronized 在 JVM 里的實(shí)現(xiàn)就是基于進(jìn)入和退出 monitor 來實(shí)現(xiàn)的,底層則是通過成對的 MonitorEnter 和 MonitorExit 指令來實(shí)現(xiàn)

      有了以上的認(rèn)識(shí),下面我們就看看鎖優(yōu)化

      Synchronized中的鎖優(yōu)化

      自適應(yīng)自旋鎖

      自旋鎖,之前我們講FutureTask源碼的時(shí)候,有一個(gè)內(nèi)部方法awaitDone(),給大家有介紹過,就是基于它實(shí)現(xiàn)的,今天再給大家總結(jié)一下。

      它的目的是為了避免阻塞和喚醒的切換,在沒有獲得鎖的時(shí)候就不進(jìn)入阻塞,不斷地循環(huán)檢測鎖是否被釋放。但是,它也有弊端,我們通常來講,一個(gè)線程占用鎖的時(shí)間相對較短,但是萬一占用很長時(shí)間怎么辦?這樣會(huì)占用大量cpu時(shí)間,這樣會(huì)導(dǎo)致性能變差,所以在1.6引入了自適應(yīng)自旋鎖來滿足這樣的場景。

      那么什么是自適應(yīng)自旋鎖呢 自旋的次數(shù)不是固定的,而是由前一次在同一個(gè)鎖上的自旋時(shí)間及鎖的擁有者的狀態(tài)來決定。如果此次自旋成功了,很有可能下一次也能成功,于是允許自旋的次數(shù)就會(huì)更多,反過來說,如果很少有線程能夠自旋成功,很有可能下一次也是失敗,則自旋次數(shù)就更少。這樣一來,就能夠更好的利用系統(tǒng)資源。

      鎖消除

      鎖消除是一種鎖的優(yōu)化策略,這種優(yōu)化更加徹底,在 JVM 編譯時(shí),通過對運(yùn)行上下文的掃描,去除不可能存在共享資源競爭的鎖。這種優(yōu)化策略可以消除沒有必要的鎖,去除獲取鎖的時(shí)間。

      鎖粗化

      如果一系列的連續(xù)加鎖解鎖操作,可能會(huì)導(dǎo)致不必要的性能損耗,所以引入鎖粗話的概念。意思是將多個(gè)連續(xù)加鎖、解鎖的操作連接在一起,擴(kuò)展成為一個(gè)范圍更大的鎖, 這個(gè)應(yīng)該很好理解

      偏向鎖

      偏向鎖是JDK 1.6引入的,它解決的場景是什么呢 我們大部分使用鎖都是解決多線程場景下的問題,但有時(shí)候往往一個(gè)線程也會(huì)存在這樣的問題,偏向鎖是在單線程執(zhí)行代碼塊時(shí)使用的機(jī)制。

      鎖的爭奪實(shí)際上是 Monitor 對象的爭奪,還有每個(gè)對象都有一個(gè)對象頭,對象頭是由 Mark Word 和 Klass pointer 組成的。一旦有線程持有了這個(gè)鎖對象,標(biāo)志位修改為 1,就進(jìn)入偏向模式,同時(shí)會(huì)把這個(gè)線程的 ID 記錄在對象的 Mark Word 中,當(dāng)同一個(gè)線程再次進(jìn)入時(shí),就不再進(jìn)行同步操作,大大減少了鎖獲取的時(shí)間,從而提高了性能。

      輕量級(jí)鎖

      我們上邊提到的偏向鎖,在多線程情況下如果偏向鎖失敗就會(huì)升級(jí)為輕量級(jí)鎖, Mark Word 的結(jié)構(gòu)也變?yōu)檩p量級(jí)鎖的結(jié)構(gòu)。

      執(zhí)行同步代碼塊之前,JVM 會(huì)在線程的棧幀中創(chuàng)建一個(gè)鎖記錄(Lock Record),并將 Mark Word 拷貝復(fù)制到鎖記錄中。然后嘗試通過 CAS 操作將 Mark Word 中的鎖記錄的指針,指向創(chuàng)建的 Lock Record。如果成功表示獲取鎖狀態(tài)成功,如果失敗,則進(jìn)入自旋獲取鎖狀態(tài)。

      如果自旋鎖失敗,就會(huì)升級(jí)為重量級(jí)鎖,也就是我們之前講的,會(huì)把線程阻塞,需等待喚醒。

      重量級(jí)鎖

      它又稱為悲觀鎖, 升級(jí)到這種情況下,鎖競爭比較激烈,占用時(shí)間也比較長,為了減少cpu的消耗,會(huì)將線程阻塞,進(jìn)入阻塞隊(duì)列。

      synchronized就是通過鎖升級(jí)策略來適應(yīng)不同的場景,所以現(xiàn)在synchronized被優(yōu)化的很好,也是我們項(xiàng)目中往往都會(huì)使用它的理由。

      結(jié)束語

      本節(jié)的內(nèi)容比較多,大家好好理解,特別是鎖的升級(jí)策略。本節(jié)我們提到了Lock鎖,下一節(jié),帶大家深入學(xué)習(xí)一下Java的Lock ~

      往期內(nèi)容推薦

      • Java多線程專題之線程與進(jìn)程概述
      • Java多線程專題之線程類和接口入門
      • Java多線程專題之進(jìn)階學(xué)習(xí)Thread(含源碼分析)
      • Java多線程專題之Callable、Future與FutureTask(含源碼分析)
      • 面試官: 有了解過線程組和線程優(yōu)先級(jí)嗎
      • 面試官: 說一下線程的生命周期過程
      • 面試官: 說一下線程間的通信
      • 面試官: 說一下Java的共享內(nèi)存模型
      • 面試官: 有了解過指令重排嗎,什么是happens-before
      • 面試官: 有了解過volatile關(guān)鍵字嗎 說說看
      • 我的博客(閱讀體驗(yàn)較佳)
      • 寫給初學(xué)者的Java基礎(chǔ)教程
      • 一文帶你快速學(xué)習(xí)Java集合類
      • 花幾分鐘快速了解一下泛型與枚舉
      • Java注解與反射入門到進(jìn)階
      • JavaIO教程從入門到進(jìn)階

      項(xiàng)目源碼(源碼已更新 歡迎star )

      • java-thread-all
      • 地址: https://github.com/qiuChengleiy/java-thread-all.git

      推薦 SpringBoot & SpringCloud (源碼已更新 歡迎star )

      • springboot-all
      • 地址: https://github.com/qiuChengleiy/springboot-all.git
      • SpringBoot系列教程合集
      • 一起來學(xué)SpringCloud合集
      鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場,版權(quán)歸原作者所有,如有侵權(quán)請聯(lián)系管理員(admin#wlmqw.com)刪除。
      上一篇 2022年6月12日 18:15
      下一篇 2022年6月12日 18:15

      相關(guān)推薦

      聯(lián)系我們

      聯(lián)系郵箱:admin#wlmqw.com
      工作時(shí)間:周一至周五,10:30-18:30,節(jié)假日休息