在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

synchronized知識合集2

jf_78858299 ? 來源:JAVA旭陽 ? 作者:JAVA旭陽 ? 2023-05-11 11:08 ? 次閱讀

synchronized修飾代碼塊

以上文SynchronizedTest2類為例子,其中synchronized關鍵字修飾代碼塊

獲取SynchronizedTest2.class的字節碼:

javac -encoding utf-8 SynchronizedTest2.java
javap -c -v SynchronizedTest2.class

Classfile /D:/ideaProjects/src/main/java/com/zj/ideaprojects/demo/test2/SynchronizedTest2.class
  Last modified 2022-10-28; size 575 bytes
  MD5 checksum ac915d460a3da67f6c76c5ed2aae01f1
  Compiled from "SynchronizedTest2.java"
public class com.zj.ideaprojects.demo.test2.SynchronizedTest2
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#18         // java/lang/Object."
   #2 = Fieldref           #19.#20        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #21            // synchronized ???? ?????
   #4 = Methodref          #22.#23        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #24            // com/zj/ideaprojects/demo/test2/SynchronizedTest2
   #6 = Class              #25            // java/lang/Object
   #7 = Utf8

我們可以發現:synchronized 同步語句塊的在字節碼中的實現,是使用了 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代碼塊的開始位置,monitorexit 指令則指明同步代碼塊的結束位置。

  1. 每個對象都擁有一個monitor,當monitor被占用時,就會處于鎖定狀態,線程執行monitorenter指令時會獲取monitor的所有權。
  2. 當monitor計數為0時,說明該monitor還未被鎖定,此時線程會進入monitor并將monitor的計數器設為1,并且該線程就是monitor的所有者。如果此線程已經獲取到了monitor鎖,再重新進入monitor鎖的話,那么會將計時器count的值加1。
  3. 如果有線程已經占用了monitor鎖,此時有其他的線程來獲取鎖,那么此線程將進入阻塞狀態,待monitor的計時器count變為0,這個線程才會獲取到monitor鎖。
  4. 只有拿到了monitor鎖對象的線程才能執行monitorexit指令。在執行 monitorexit 指令后,將鎖計數器設為 0,表明鎖被釋放,其他線程可以嘗試獲取鎖。
  5. 如果獲取對象鎖失敗,那當前線程就要阻塞等待,直到鎖被另外一個線程釋放為止

有個奇怪的現象不知道大家有沒有發現?為什么monitorenter指令只出現了一次,但是monitorexit指令卻出現了2次?

因為編譯器必須保證無論同步代碼塊中的代碼以何種方式結束,代碼中每次調用monitorenter必須執行對應的monitorexit指令。如果沒有執行 monitorexit指令,monitor一直被占用,其他線程都無法獲取,這是非常危險的。

這個就很像"try catch finally"中的finally,不管程序運行結果如何,必須要執行monitorexit指令,釋放monitor所有權

小結一下:

  1. 同步代碼塊是通過monitorenter和monitorexit指令來實現;同步方式是通過方法中的access_flags中設置ACC_SYNCHRONIZED標識符來實現,ACC_SYNCHRONIZED標識符會去隱式調用這兩個指令:monitorenter和monitorexit
  2. synchronized修飾方法、修飾代碼塊 ,歸根到底,都是通過競爭monitor所有權來實現同步的
  3. 每個java對象都會與一個monitor相關聯,可以由線程獲取和釋放
  4. monitor通過維護一個計數器來記錄鎖的獲取,重入,釋放情況

鎖優化

為什么說JDK早期,Synchronized是重量級鎖呢?在JVM中monitorenter和monitorexit字節碼依賴于底層的操作系統Mutex Lock來實現的,但是由于使用Mutex Lock需要將 當前線程掛起并從用戶態切換到內核態來申請鎖資源,還需要經過一個中斷的調用,申請完之后還需要從內核態返回到用戶態 。整個切換過程是非常消耗資源的,如果程序中存在大量的鎖競爭,那么會引起程序頻繁的在用戶態和內核態進行切換,嚴重影響到程序的性能。

Linux系統架構中可以分為用戶空間和內核,我們的程序都運行在用戶空間,進入用戶運行狀態就是所謂的用戶態。在用戶態可能會涉及到某些操作如I/O調用,就會進入內核中運行,此時進程就被稱為內核運行態,簡稱內核態。

  1. 內核: 本質上可以理解為一種軟件,控制計算機的硬件資源,并提供上層應用程序運行的環境。
  2. 用戶空間: 上層應用程序活動的空間。應用程序的執行必須依托于內核提供的資源,包括CPU資源、存儲資源、I/O資源等。
  3. 系統調用: 為了使上層應用能夠訪問到這些資源,內核必須為上層應用提供訪問的接口:即系統調用。

為了解決這一問題,在JDK1.6對Synchronized進行大量的優化 鎖自旋、鎖粗化、鎖消除,鎖膨脹等技術,在這部分擴展內容比較多,我們接下來一一道來。

自旋鎖

在jdk1.6前多線程競爭鎖時,當一個線程A獲取鎖時,它會阻塞其他所有正在競爭的線程,這樣對性能帶來了極大的影響。在掛起線程和恢復線程的操作都需要轉入內核態中完成,這些操作對系統的并發性能帶來了很大的壓力。由于在實際環境中, 很多線程的鎖定狀態只會持續很短的一段時間,會很快釋放鎖 ,為了如此短暫的時間去掛起和阻塞其他所有競爭鎖的線程,是非常浪費資源的,我們完全可以讓另一個沒有獲取到鎖的線程在門外等待一會(自旋),但 不放棄CPU的執行時間 ,等待持有鎖的線程A釋放鎖,就里面去獲得鎖。這其實就是自旋鎖

但是我們也無法保證線程獲取鎖之后,就一定很快釋放鎖。萬一遇到有線程,長時間不釋放鎖,其會帶來更多的性能開銷。因為在線程自旋時,始終會占用CPU的時間片,如果鎖占用的時間太長,那么自旋的線程會消耗掉CPU資源。 所以我們需要對鎖自旋的次數有所限制,如果自旋超過了限定的次數仍然沒有成功獲取到鎖,就應該重新使用傳統的方式去掛起線程了 。在JDK定義中,自旋鎖默認的自旋次數為10次,用戶可以使用參數-XX:PreBlockSpin來更改。

后來也有改進型的 自適應自旋鎖, 自適應意味著自旋的次數不在固定,而是由前一次在同一個鎖上的自旋時間和鎖的擁有者的狀態共同決定。如果在同一個鎖對象上,自旋等待剛剛成功獲得過鎖,并且持有鎖的線程正在運行中,那么虛擬機就會認為這次自旋也是很可能再次成功的,進而它將會允許線程自旋相對更長的時間。如果對于某個鎖,線程很少成功獲得過,則會相應減少自旋的時間甚至直接進入阻塞的狀態,避免浪費處理器資源。筆者感覺這個跟CPU的分支預測,有異曲同工之妙

鎖粗化

一般來說,同步塊的作用范圍應該盡可能小,縮短阻塞時間,如果存在鎖競爭,那么等待鎖的線程也能盡快獲取鎖 但某些情況下,可能會對同一個鎖頻繁訪問,或者有人在循環里面寫上了synchronized關鍵字,為了降低短時間內大量的鎖請求、釋放帶來的性能損耗,Java虛擬機發現了之后會 適當擴大加鎖的范圍,以避免頻繁的拿鎖釋放鎖的過程 。將多個鎖請求合并為一個請求,這就是鎖粗化

public class LockCoarseningTest {
 public String test() {
  StringBuffer sb = new StringBuffer();
  for(int i = 0; i < 100; i++) {
   sb.append("test");
  }
  return sb.toString();
 }
}

append() 為同步方法,短時間內大量進行鎖請求、鎖釋放,JVM 會自動進行鎖粗化,將加鎖范圍擴大至 for 循環外部,從而只需要進行一次鎖請求、鎖釋放

鎖消除

鎖消除:通過運行時JIT編譯器的逃逸分析來消除一些沒有在當前同步塊以外被其他線程共享的數據的鎖保護,通過逃逸分析也可以在線程本的Stack上進行對象空間的分配(同時還可以減少Heap上的垃圾收集開銷)。其實就是即時編譯器通過對運行上下文的掃描,對不可能存在共享資源競爭的鎖進行消除,從而節約大量的資源開銷,提高效率

public class LockEliminateTest {
 static int i = 0;
 
 public void method1() {
  i++;
 }
 
 public void method2() {
  Object obj = new Object();
  synchronized (obj) {
   i++;
  }
 }
}

method2() 方法中的 obj 為局部變量,顯然不可能被共享,對其加鎖也毫無意義,故被即時編譯器消除

鎖膨脹

鎖膨脹方向:無鎖 → 偏向鎖 → 輕量級鎖 → 重量級鎖偏向鎖、輕量級鎖,這兩個鎖既是一種優化策略,也是一種膨脹過程,接下來我們分別聊聊

偏向鎖

在大多數情況下雖然加了鎖,但是沒有鎖競爭的發生,甚至是同一個線程反復獲得這個鎖,那么多次的獲取鎖和釋放鎖會帶來很多不必要的性能開銷和上下文切換。偏向鎖就為了針對這種情況而出現的

偏向鎖指, 鎖偏向于第一個獲取他的線程 ,若接下來的執行過程中,該鎖一直沒有被其他線程獲取,則持有偏向鎖的線程永遠不需要再進行同步。 這樣就在無鎖競爭的情況下避免在鎖獲取過程中執行不必要的獲取鎖和釋放鎖操作

偏向鎖的具體過程:

  1. 首先JVM要設置為可用偏向鎖。然后當一個進程訪問同步塊并且獲得鎖的時候,會在對象頭和棧幀的鎖記錄里面存儲取得偏向鎖的線程ID。
  2. 等下一次有線程嘗試獲取鎖的時候,首先檢查這個對象頭的MarkWord是不是儲存著這個線程的ID。如果是,那么直接進去而不需要任何別的操作。
  3. 如果不是,那么分為兩種情況:
  • 對象的偏向鎖標志位為0(當前不是偏向鎖),說明發生了競爭,已經膨脹為輕量級鎖,這時使用CAS操作嘗試獲得鎖。
  • 偏向鎖標志位為1,說明還是偏向鎖不過請求的線程不是原來那個了。這時只需要使用CAS嘗試把對象頭偏向鎖從原來那個線程指向目前求鎖的線程。

輕量級鎖

在實際情況中,大部分的鎖,在整個同步生命周期內都不存在競爭,在無鎖競爭的情況下完全可以避免調用操作系統層面的 重量級互斥鎖, 可以通過CAS原子指令就可以完成鎖的獲取及釋放。當存在鎖競爭的情況下,執行CAS指令失敗的線程將調用操作系統互斥鎖進入到阻塞狀態,當鎖被釋放的時候被喚醒。當升級為輕量級鎖之后,MarkWord的結構也會隨之變為輕量級鎖結構。JVM會利用CAS嘗試把對象原本的MarkWord 更新為Lock Record的指針,成功就說明加鎖成功,改變鎖標志位為00,然后執行相關同步操作。輕量級鎖所適應的場景是 線程交替執行同步塊的場合 ,如果存在同一時間訪問同一鎖的場合,就會導致輕量級鎖就會失效,進而膨脹為重量級鎖。

CAS (Compare-And-Swap):顧名思義 比較并替換 。這是一個由CPU硬件提供并實現的原子操作.可以被認為是一種 樂觀鎖 ,會以一種更加樂觀的態度對待事情,認為自己可以操作成功。當多個線程操作同一個共享資源時,僅能有一個線程同一時間獲得鎖成功,在樂觀鎖中,其他線程發現自己無法成功獲得鎖,并不會像悲觀鎖那樣阻塞線程,而是直接返回,可以去選擇再次重試獲得鎖,也可以直接退出

CAS機制所保證的只是一個變量的原子性操作,無法保證整個代碼塊的原子性

最后再小結一下,鎖的優缺點對比:

優點 缺點 使用場景
偏向鎖 加鎖和解鎖不需要CAS操作,沒有額外的性能消耗,和執行非同步方法相比僅存在納秒級的差距 如果線程間存在鎖競爭,會帶來額外的鎖撤銷的消耗 適用于只有一個線程訪問同步塊的場景
輕量級鎖 競爭的線程不會阻塞,提高了響應速度 如線程成始終得不到鎖競爭的線程,使用自旋會消耗CPU性能 追求響應時間,同步塊執行速度非常快
重量級鎖 線程競爭不適用自旋,不會消耗CPU 線程阻塞,響應時間緩慢,在多線程下,頻繁的獲取釋放鎖,會帶來巨大的性能消耗 追求吞吐量,同步塊執行速度較長

最高效的是偏向鎖,盡量使用偏向鎖,如果不能(發生了競爭)就膨脹為輕量級鎖,當發生鎖競爭時,輕量級鎖的CAS操作會自動失效,鎖再次膨脹為重量級鎖。 鎖一般是只能升級但不能降級 ,這種鎖升級卻不能降級的策略,目的是 為了提高獲得鎖和釋放鎖的效率。( hotspot其實是可以發生鎖降級的,但觸發鎖降級的條件比較苛刻**)**

偏向鎖,輕量級鎖,只需在用戶態就可以實現,而不需要進行用戶態和內核態之間的切換

經過如此多的鎖優化,如今的 synchronized 鎖效率非常不錯,目前不論是各種開源框架還是 JDK 源碼都大量使用了 synchronized 關鍵字。

synchronized關鍵字實現單例模式

我們來看一個經典的例子,利用synchronized關鍵字實現單例模式

/**
 * 懶漢 - 雙層校驗鎖
 */
public class SingleDoubleCheck {
    private static SingleDoubleCheck instance = null;

    private SingleDoubleCheck(){}//將構造器 私有化,防止外部調用

    public static SingleDoubleCheck getInstance() {
        if (instance == null) { //part 1
            synchronized (SingleDoubleCheck.class) {
                if (instance == null) { //part 2
                    instance = new SingleDoubleCheck();//part 3
                }
            }
        }
        return instance;
    }
}

對單例模式感興趣的話,見拓展:https://mp.weixin.qq.com/s/TyiCfVMeeDwa-2hd9N9XJQ

synchronized 和 volatile 的區別?

synchronized 關鍵字和 volatile 關鍵字是兩個互補的存在,而不是對立的存在

  1. volatile 關鍵字是線程同步的輕量級實現,所以 volatile性能肯定比synchronized關鍵字要好 。但是 volatile 關鍵字只能用于變量而 synchronized 關鍵字可以修飾方法以及代碼塊 。
  2. volatile 關鍵字能保證數據的可見性,但不能保證數據的原子性。synchronized 關鍵字兩者都能保證。
  3. volatile關鍵字主要用于解決變量在多個線程之間的可見性,而 synchronized 關鍵字解決的是多個線程之間訪問資源的同步性。
  4. volatile只能修飾實例變量和類變量,而synchronized可以修飾方法,以及代碼塊。

尾語

本文拓展內容確實有點多,很開心你能看到最后,我們再簡明地回顧一下synchronized 的特性

  1. 原子性:確保線程互斥的訪問同步代碼。synchronized保證只有一個線程拿到鎖,進入同步代碼塊操作共享資源,因此具有原子性。
  2. 可見性:保證共享變量的修改能夠及時可見。當某線程進入synchronized代碼塊前后,線程會獲得鎖,清空工作內存,從主內存拷貝共享變量最新的值到工作內存成為副本,執行代碼,將修改后的副本的值刷新回主內存中,線程釋放鎖。其他獲取不到鎖的線程會阻塞等待,所以變量的值一直都是最新的。
  3. 有序性:synchronized內的代碼和外部的代碼禁止排序,至于內部的代碼,則不會禁止排序,但是由于只有一個線程進入同步代碼塊,因此在同步代碼塊中相當于是單線程的,根據 as-if-serial 語義,即使代碼塊內發生了重排序,也不會影響程序執行的結果。
  4. 悲觀鎖:synchronized是悲觀鎖。每次使用共享資源時都認為會和其他線程產生競爭,所以每次使用共享資源都會上鎖。
  5. 獨占鎖(排他鎖):synchronized是獨占鎖(排他鎖)。該鎖一次只能被一個線程所持有,其他線程被阻塞。
  6. 非公平鎖:synchronized是非公平鎖。線程獲取鎖的順序可以不按照線程的阻塞順序。允許新來的線程有可能立即獲得監視器,而在等待區中等候已久的線程可能再次等待。這樣有利于提高性能,但是也可能會導致饑餓現象
  7. 可重入鎖:synchronized是可重入鎖。持鎖線程可以再次獲取自己的內部的鎖,可一定程度避免死鎖。

參考資料

https://openjdk.org/groups/hotspot/docs/HotSpotGlossary.html

《深入理解java虛擬機》

《Java并發編程的藝術》

https://www.cnblogs.com/qingshan-tang/p/12698705.html

https://www.cnblogs.com/jajian/p/13681781.html

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • JAVA
    +關注

    關注

    19

    文章

    2974

    瀏覽量

    105002
  • 代碼
    +關注

    關注

    30

    文章

    4823

    瀏覽量

    68916
  • 線程安全
    +關注

    關注

    0

    文章

    13

    瀏覽量

    2471
收藏 人收藏

    評論

    相關推薦

    關于labview的論文合集2

    關于labview的論文合集2
    發表于 09-23 13:47

    求解有沒有類似于電子發燒友這樣的分版塊的知識合集

    求解有沒有類似于電子發燒友這樣的分版塊的知識合集
    發表于 09-27 00:48

    有線通信的知識點大合集,絕對實用

    有線通信的知識點大合集,絕對實用
    發表于 01-17 08:24

    SPI協議的知識點大合集,絕對實用

    SPI協議的知識點大合集,絕對實用
    發表于 02-17 07:31

    stm32f103中的電路知識點大合集,錯過后悔

    stm32f103中的電路知識點大合集,錯過后悔
    發表于 02-21 06:49

    Synchronized multi-spark modul

    Synchronized multi-spark module (SMSM) for Electronic Ignition Devices (EID)
    發表于 12-29 09:09 ?851次閱讀
    <b class='flag-5'>Synchronized</b> multi-spark modul

    學習單片機的必備基礎知識合集免費下載

    本文檔的主要內容詳細介紹的是學習單片機的必備基礎知識合集免費下載包括了:1.單片機的大致介紹, 2.通俗定義 3.51系列產品 4.標號意思 5.引腳介紹 6.用C語言開發的部分信息
    發表于 06-11 17:48 ?4次下載
    學習單片機的必備基礎<b class='flag-5'>知識</b><b class='flag-5'>合集</b>免費下載

    電工技術基礎知識教程合集免費下載

    本文檔的主要內容詳細介紹的是電工技術基礎知識教程合集免費下載包括了:常用低壓供配電系統 ,電工安全的基本知識 ,常用儀表與測量 。
    發表于 08-24 08:00 ?136次下載
    電工技術基礎<b class='flag-5'>知識</b>教程<b class='flag-5'>合集</b>免費下載

    電力基礎知識合集

    電力基礎知識合集
    發表于 03-14 16:35 ?0次下載

    詳細介紹synchronized和Object的關鍵方法和虛擬機實現原理

    編程過程中經常會遇到線程的同步問題,Java 中對同步問題的解決方案比較多(synchronized、JUC、原子操作、volatile、條件變量等),其中synchronized 最方便、簡單易用,也是java 編程中使用最多的臨界區保護方案。
    的頭像 發表于 03-13 10:06 ?1306次閱讀

    synchronized知識合集1

    * 線程安全 * 什么是synchronized關鍵字? * synchronized實現方式 * 1.修飾實例方法 * 2.修飾靜態方法 * 3.修飾代碼塊
    的頭像 發表于 05-11 11:07 ?479次閱讀
    <b class='flag-5'>synchronized</b><b class='flag-5'>知識</b><b class='flag-5'>合集</b>1

    synchronized的原理與四種用法介紹

    JDK提供的鎖分兩種,一種是JVM實現的synchronized,是java的關鍵字,因此在這個關鍵字作用對象的范圍內都是可以保證原子性的,主要是依賴特殊的CPU指令。另一種是JDK提供的代碼層面的鎖Lock。
    的頭像 發表于 06-09 16:13 ?1164次閱讀
    <b class='flag-5'>synchronized</b>的原理與四種用法介紹

    synchronized 的幾種錯誤用法

    synchronized 在我們平常工作中也是挺常用的, 對于擺脫多線程問題很有幫助。但是如果synchronized被錯誤使用時,可能會給我們帶來很多麻煩。 在本文中,我們將討論與同步相關的一些
    的頭像 發表于 10-09 10:25 ?741次閱讀

    synchronized的鎖膨脹

    synchronized void sync1 ( ) { } // 鎖的是SynchronizedTest.class對象 public static void sync2 ( ) { synchronized
    的頭像 發表于 10-10 16:58 ?515次閱讀
    <b class='flag-5'>synchronized</b>的鎖膨脹

    C語言編程必備知識合集

    電子發燒友網站提供《C語言編程必備知識合集.zip》資料免費下載
    發表于 11-21 09:34 ?0次下載
    C語言編程必備<b class='flag-5'>知識</b><b class='flag-5'>合集</b>
    主站蜘蛛池模板: 欧美亚洲综合图区在线 | 国产做a爰片久久毛片 | 在线成人aa在线看片 | 亚洲国产人成在线观看 | 丁香婷婷在线视频 | 日本一卡二卡≡卡四卡精品 | 男女视频在线 | 又大又粗又爽黄毛片 | 伊人色强在线网 | 天天干狠狠 | 日韩毛片 | 国产精品天天影视久久综合网 | 色噜噜狠狠成人中文小说 | 久久综合久久88 | 国产aaaaaa | 亚洲区一二三四区2021 | 欧美网站在线播放 | 97精品伊人久久久大香线焦 | 亚洲香蕉国产高清在线播放 | 极品美女啪啪 | 美女视频黄a全部免费看小说 | 久久久久久久久综合 | 国产99在线播放免费 | 免费视频不卡一区二区三区 | 午夜黄色毛片 | 深夜释放自己vlog糖心旧版本 | 欧美男女交性过程视频 | 婷婷六月激情 | 在线视频一区二区 | 欧美色a电影精品aaaa | 免费黄色大片视频 | 色播亚洲 | 国产精品理论 | 狼色视频在线观免费观看 | 亚洲一本高清 | 国语对白一区二区三区 | 日韩欧美亚洲综合久久影院d3 | 午夜两性色视频免费网站 | 久久在草 | 中文字幕1区 | 亚洲激情a |