溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JVM虛擬機內存模型與高效并發(fā)知識點有哪些

發(fā)布時間:2022-01-04 17:08:30 來源:億速云 閱讀:104 作者:iii 欄目:編程語言

這篇文章主要講解了“JVM虛擬機內存模型與高效并發(fā)知識點有哪些”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“JVM虛擬機內存模型與高效并發(fā)知識點有哪些”吧!

Java內存模型,即Java Memory Model,簡稱 JMM ,它是一種抽象的概念,或者是一種協(xié)議,用來解決在并發(fā)編程過程中內存訪問的問題,同時又可以兼容不同的硬件和操作系統(tǒng),JMM的原理與硬件一致性的原理類似。在硬件一致性的實現(xiàn)中,每個CPU會存在一個高速緩存,并且各個CPU通過與自己的高速緩存交互來向共享內存中讀寫數(shù)據(jù)。

如下圖所示,在Java內存模型中,所有的變量都存儲在主內存。每個Java線程都存在著自己的工作內存,工作內存中保存了該線程用得到的變量的副本,線程對變量的讀寫都在工作內存中完成,無法直接操作主內存,也無法直接訪問其他線程的工作內存。當一個線程之間的變量的值的傳遞必須經(jīng)過主內存。

當兩個線程A和線程B之間要完成通信的話,要經(jīng)歷如下兩步:

  1. 線程A從主內存中將共享變量讀入線程A的工作內存后并進行操作,之后將數(shù)據(jù)重新寫回到主內存中;

  2. 線程B從主存中讀取最新的共享變量

volatile關鍵字使得每次volatile變量都能夠強制刷新到主存,從而對每個線程都是可見的。

JVM虛擬機內存模型與高效并發(fā)知識點有哪些

需要注意的是,JMM與Java內存區(qū)域的劃分是不同的概念層次,更恰當說JMM描述的是一組規(guī)則,通過這組規(guī)則控制程序中各個變量在共享數(shù)據(jù)區(qū)域和私有數(shù)據(jù)區(qū)域的訪問方式。在JMM中主內存屬于共享數(shù)據(jù)區(qū)域,從某個程度上講應該包括了堆和方法區(qū),而工作內存數(shù)據(jù)線程私有數(shù)據(jù)區(qū)域,從某個程度上講則應該包括程序計數(shù)器、虛擬機棧以及本地方法棧。

內存間交互的操作

上面介紹了JMM中主內存和工作內存交互以及線程之間通信的原理,但是具體到各個內存之間如何進行變量的傳遞,JMM定義了8種操作,用來實現(xiàn)主內存與工作內存之間的具體交互協(xié)議:

lock
unlock
read
load
use
assign
store
write

如果要把一個變量從主內存中復制到工作內存,就需要按順尋地執(zhí)行 read 和 load 操作,如果把變量從工作內存中同步回主內存中,就要按順序地執(zhí)行 store 和 writ e操作。Java內存模型只要求上述兩個操作必須按順序執(zhí)行,而沒有保證必須是連續(xù)執(zhí)行。也就是 read 和 load 之間, store 和 write 之間是可以插入其他指令的,如對主內存中的變量 a 、 b 進行訪問時,可能的順序是 read a , read b , load b , load a 。

Java內存模型還規(guī)定了在執(zhí)行上述八種基本操作時,必須滿足如下規(guī)則:

  1. 不允許 read 和 load 、 store 和 write 操作之一單獨出現(xiàn);

  2. 不允許一個線程丟棄它的最近 assign 的操作,即變量在工作內存中改變了之后必須同步到主內存中;

  3. 不允許一個線程無原因地(沒有發(fā)生過任何 assign 操作)把數(shù)據(jù)從工作內存同步回主內存中;

  4. 一個新的變量只能在主內存中誕生,不允許在工作內存中直接使用一個未被初始化(load或assign)的變量。即就是對一個變量實施 use 和 store 操作之前,必須先執(zhí)行過了 assign 和 load 操作;

  5. 一個變量在同一時刻只允許一條線程對其進行 lock 操作, lock 和 unlock 必須成對出現(xiàn);

  6. 如果對一個變量執(zhí)行 lock 操作,將會清空工作內存中此變量的值,在執(zhí)行引擎使用這個變量前需要重新執(zhí)行 load 或 assign 操作初始化變量的值;

  7. 如果一個變量事先沒有被 lock 操作鎖定,則不允許對它執(zhí)行 unlock 操作,也不允許去unlock一個被其他線程鎖定的變量;

  8. 對一個變量執(zhí)行 unlock 操作之前,必須先把此變量同步到主內存中(執(zhí)行 store 和 write操作)。

此外,虛擬機還對voliate關鍵字和long及double做了一些特殊的規(guī)定。

voliate關鍵字的兩個作用
  1. 保證變量的可見性:當一個被voliate關鍵字修飾的變量被一個線程修改的時候,其他線程可以立刻得到修改之后的結果。當一個線程向被voliate關鍵字修飾的變量寫入數(shù)據(jù)的時候,虛擬機會強制它被值刷新到主內存中。當一個線程用到被voliate關鍵字修飾的值的時候,虛擬機會強制要求它從主內存中讀取。

  2. 屏蔽指令重排序:指令重排序是編譯器和處理器為了高效對程序進行優(yōu)化的手段,它只能保證程序執(zhí)行的結果時正確的,但是無法保證程序的操作順序與代碼順序一致。這在單線程中不會構成問題,但是在多線程中就會出現(xiàn)問題。非常經(jīng)典的例子是在單例方法中同時對字段加入voliate,就是為了防止指令重排序。為了說明這一點,可以看下面的例子。

我們以下面的程序為例來說明voliate是如何防止指令重排序:

public class Singleton {
    private volatile static Singleton singleton;
    private Singleton() {}
    public static Singleton getInstance() {
        if (singleton == null) { // 1
            sychronized(Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton(); // 2
                }
            }
        }
        return singleton;
    }
} 
復制代碼

實際上當程序執(zhí)行到2處的時候,如果我們沒有使用voliate關鍵字修飾變量singleton,就可能會造成錯誤。這是因為使用 new 關鍵字初始化一個對象的過程并不是一個原子的操作,它分成下面三個步驟進行:

  1. 給 singleton 分配內存

  2. 調用 Singleton 的構造函數(shù)來初始化成員變量

  3. 將 singleton 對象指向分配的內存空間(執(zhí)行完這步 singleton 就為非 null 了)

如果虛擬機存在指令重排序優(yōu)化,則步驟2和3的順序是無法確定的。如果A線程率先進入同步代碼塊并先執(zhí)行了3而沒有執(zhí)行2,此時因為singleton已經(jīng)非null。這時候線程B到了1處,判斷singleton非null并將其返回使用,因為此時Singleton實際上還未初始化,自然就會出錯。

但是特別注意在jdk 1.5以前的版本使用了volatile的雙檢鎖還是有問題的。其原因是Java 5以前的JMM(Java 內存模型)是存在缺陷的,即時將變量聲明成volatile也不能完全避免重排序,主要是volatile變量前后的代碼仍然存在重排序問題。這個volatile屏蔽重排序的問題在jdk 1.5 (JSR-133)中才得以修復,這時候jdk對volatile增強了語義,對volatile對象都會加入讀寫的內存屏障,以此來保證可見性,這時候2-3就變成了代碼序而不會被CPU重排,所以在這之后才可以放心使用volatile。

對long及double的特殊規(guī)定

虛擬機除了對voliate關鍵字做了特殊規(guī)定,還對long及double做了一些特殊的規(guī)定:允許沒有被volatile修飾的long和double類型的變量讀寫操作分成兩個32位操作。也就是說,對long和double的讀寫是非原子的,它是分成兩個步驟來進行的。但是,你可以通過將它們聲明為voliate的來保證對它們的讀寫的原子性。

先行發(fā)生原則(happens-before) & as-if-serial

Java內存模型是通過各種操作定義的,JMM為程序中所有的操作定義了一個偏序關系,就是先行發(fā)生原則(Happens-before)。它是判斷數(shù)據(jù)是否存在競爭、線程是否安全的主要依據(jù)。想要保證執(zhí)行操作B的線程看到操作A的結果,那么在A和B之間必須滿足Happens-before關系,否則JVM就可以對它們任意地排序。

先行發(fā)生原則主要包括下面幾項,當兩個變量之間滿足以下關系中的任意一個的時候,我們就可以判斷它們之間的是存在先后順序的,串行執(zhí)行的。

程序次序規(guī)則(Program Order Rule)
管理鎖定規(guī)則(Monitor Lock Rule)
volatile變量規(guī)則(Volatile Variable Rule)
線程啟動規(guī)則(Thread Start Rule)
線程終止規(guī)則(Thread Termination Rule)
線程中斷規(guī)則(Thread Interruption Rule)
對象終結規(guī)則(Finilizer Rule)
傳遞性(Transitivity)

不同操作時間先后順序與先行發(fā)生原則之間沒有關系,二者不能相互推斷,衡量并發(fā)安全問題不能受到時間順序的干擾,一切都要以先行發(fā)生原則為準。

如果兩個操作訪問同一個變量,且這兩個操作有一個為寫操作,此時這兩個操作就存在數(shù)據(jù)依賴性這里就存在三種情況:1).讀后寫;2).寫后寫;3). 寫后讀,三種操作都是存在數(shù)據(jù)依賴性的,如果重排序會對最終執(zhí)行結果會存在影響。編譯器和處理器在重排序時,會遵守數(shù)據(jù)依賴性,編譯器和處理器不會改變存在數(shù)據(jù)依賴性關系的兩個操作的執(zhí)行順序。

還有就是 as-if-serial 語義:不管怎么重排序(編譯器和處理器為了提供并行度),(單線程)程序的執(zhí)行結果不能被改變。編譯器,runtime和處理器都必須遵守as-if-serial語義。as-if-serial語義保證單線程內程序的執(zhí)行結果不被改變,happens-before關系保證正確同步的多線程程序的執(zhí)行結果不被改變。

先行發(fā)生原則(happens-before)和as-if-serial語義是虛擬機為了保證執(zhí)行結果不變的情況下提供程序的并行度優(yōu)化所遵循的原則,前者適用于多線程的情形,后者適用于單線程的環(huán)境。

在此我向大家推薦一個Java高級群 :725633148 里面會分享一些資深架構師錄制的視頻錄像:(有Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務架構的原理,JVM性能優(yōu)化、分布式架構)等這些成為架構師必備的知識體系 進群馬上免費領取,目前受益良多!

2、Java線程

2.1 Java線程的實現(xiàn)

在Window系統(tǒng)和Linux系統(tǒng)上,Java線程的實現(xiàn)是基于一對一的線程模型,所謂的一對一模型,實際上就是通過語言級別層面程序去間接調用系統(tǒng)內核的線程模型,即我們在使用Java線程時,Java虛擬機內部是轉而調用當前操作系統(tǒng)的內核線程來完成當前任務。這里需要了解一個術語,內核線程(Kernel-Level Thread,KLT),它是由操作系統(tǒng)內核(Kernel)支持的線程,這種線程是由操作系統(tǒng)內核來完成線程切換,內核通過操作調度器進而對線程執(zhí)行調度,并將線程的任務映射到各個處理器上。每個內核線程可以視為內核的一個分身,這也就是操作系統(tǒng)可以同時處理多任務的原因。由于我們編寫的多線程程序屬于語言層面的,程序一般不會直接去調用內核線程,取而代之的是一種輕量級的進程(Light Weight Process),也是通常意義上的線程,由于每個輕量級進程都會映射到一個內核線程,因此我們可以通過輕量級進程調用內核線程,進而由操作系統(tǒng)內核將任務映射到各個處理器,這種輕量級進程與內核線程間1對1的關系就稱為一對一的線程模型。

JVM虛擬機內存模型與高效并發(fā)知識點有哪些

如圖所示,每個線程最終都會映射到CPU中進行處理,如果CPU存在多核,那么一個CPU將可以并行執(zhí)行多個線程任務。

2.2 線程安全

Java中可以使用三種方式來保障程序的線程安全:1).互斥同步;2).非阻塞同步;3).無同步。

互斥同步

在Java中最基本的使用同步方式是使用 sychronized 關鍵字,該關鍵字在被編譯之后會在同步代碼塊前后形成 monitorenter 和 monitorexit 字節(jié)碼指令。這兩個字節(jié)碼都需要一個reference類型的參數(shù)來指明要鎖定和解鎖的對象。如果在Java程序中明確指定了對象參數(shù),就會使用該對象,否則就會根據(jù)sychronized修飾的是實例方法還是類方法,去去對象實例或者Class對象作為加鎖對象。

synchronized先天具有 重入性 :根據(jù)虛擬機的要求,在執(zhí)行sychronized指令時,首先要嘗試獲取對象的鎖。如果這個對象沒有被鎖定,或者當前線程已經(jīng)擁有了該對象的鎖,就把鎖的計數(shù)器加1,相應地執(zhí)行 monitorexit 指令時會將鎖的計數(shù)器減1,當計數(shù)器為0時就釋放鎖。弱獲取對象鎖失敗,那當前線程就要阻塞等待,直到對象鎖被另外一個線程釋放為止。

除了使用sychronized,我們還可以使用JUC中的ReentrantLock來實現(xiàn)同步,它與sychronized類似,區(qū)別主要表現(xiàn)在以下3個方面:

  1. 等待可中斷:當持有鎖的線程長期不釋放鎖的時候,正在等待的線程可以選擇放棄等待;

  2. 公平鎖:多個線程等待同一個鎖時,必須按照申請鎖的時間順序來依次獲得鎖;而非公平鎖無法保證,當鎖被釋放時任何在等待的線程都可以獲得鎖。sychronized本身時非公平鎖,而ReentrantLock默認是非公平的,可以通過構造函數(shù)要求其為公平的。

  3. 鎖可以綁定多個條件:ReentrantLock可以綁定多個Condition對象,而sychronized要與多個條件關聯(lián)就不得不加一個鎖,ReentrantLock只要多次調用newCondition即可。

在JDK1.5之前,sychronized在多線程環(huán)境下比ReentrantLock要差一些,但是在JDK1.6以上,虛擬機對sychronized的性能進行了優(yōu)化,性能不再是使用ReentrantLock替代sychronized的主要因素。

非阻塞同步

所謂非阻塞同步就是在實現(xiàn)同步的過程中無需將線程掛起,它是相對于互斥同步而言的。互斥同步本質上是一種悲觀的并發(fā)策略,而非阻塞同步是一種樂觀的并發(fā)策略。在JUC中的許多并發(fā)組建都是基于CAS原理實現(xiàn)的,所謂CAS就是Compare-And-Swape,類似于樂觀加鎖。但與我們熟知的樂觀鎖不同的是,它在判斷的時候會涉及到3個值:“新值”、“舊值”和“內存中的值”,在實現(xiàn)的時候會使用一個無限循環(huán),每次拿“舊值”與“內存中的值”進行比較,如果兩個值一樣就說明“內存中的值”沒有被其他線程修改過,否則就被修改過,需要重新讀取內存中的值為“舊值”,再拿“舊值”與“內存中的值”進行判斷。直到“舊值”與“內存中的值”一樣,就把“新值”更新到內存當中。

這里要注意上面的CAS操作是分3個步驟的,但是這3個步驟必須一次性完成,因為不然的話,當判斷“內存中的值”與“舊值”相等之后,向內存寫入“新值”之間被其他線程修改就可能會得到錯誤的結果。JDK中的 sun.misc.Unsafe 中的 compareAndSwapInt 等一系列方法Native就是用來完成這種操作的。另外還要注意,上面的CAS操作存在一些問題:

AtomicReference
無同步方案

所謂無同步方案就是不需要同步,比如一些集合屬于不可變集合,那么就沒有必要對其進行同步。有一些方法,它的作用就是一個函數(shù),這在函數(shù)式編程思想里面比較常見,這種函數(shù)通過輸入就可以預知輸出,而且參與計算的變量都是局部變量等,所以也沒必要進行同步。還有一種就是線程局部變量,比如ThreadLocal等。

2.3 鎖優(yōu)化

自旋鎖和自適應自旋

自旋鎖用來解決互斥同步過程中線程切換的問題,因為線程切換本身是存在一定的開銷的。如果物理機器有一個以上的處理器,能讓兩個或以上的線程同時并行執(zhí)行,我們就可以讓后面請求鎖的那個線程“稍等一會”,但不放棄處理器的執(zhí)行時間,看看持有鎖的線程是否很快就會釋放鎖。為了讓線程等待,我們只須讓線程執(zhí)行一個忙循環(huán)(自旋),這項技術就是所謂的自旋鎖。

自旋鎖在JDK 1.4.2中就已經(jīng)引入,只不過默認是關閉的,可以使用 -XX:+UseSpinnin g參數(shù)來開啟,在JDK 1.6中就已經(jīng)改為默認開啟了。自旋等待本身雖然避免了線程切換的開銷,但它是要占用處理器時間的, 所以如果鎖被占用的時間很短,自旋等待的效果就會非常好,反之如果鎖被占用的時間很長,那么自旋的線程只會白白消耗處理器資源,而不會做任何有用的工作, 反而會帶來性能的浪費。

我們可以通過參數(shù) -XX:PreBlockSpin 來指定自旋的次數(shù),默認值是10次。在JDK 1.6中引入了 自適應的自旋鎖 。自適應意味著自旋的時間不再固定了,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態(tài)來決定。如果在同一個鎖對象上,自旋等待剛剛成功獲得過鎖,并且持有鎖的線程正在運行中,那么虛擬機就會認為這次自旋也很有可能再次成功,進而它將允許自旋等待持續(xù)相對更長的時間, 比如100個循環(huán)。另一方面,如果對于某個鎖,自旋很少成功獲得過,那在以后要獲取這個鎖時將可能省略掉自旋過程,以避免浪費處理器資源。

下面是自旋鎖的一種實現(xiàn)的例子:

public class SpinLock {
    private AtomicReference<Thread> sign = new AtomicReference<>();
    public void lock() {
        Thread current = Thread.currentThread();
        while(!sign.compareAndSet(null, current)) ;
    }
    public void unlock() {
        Thread current = Thread.currentThread();
        sign.compareAndSet(current, null);
    }
}
復制代碼

從上面的例子我們可以看出,自旋鎖是通過CAS操作,通過比較期值是否符合預期來加鎖和釋放鎖的。在lock方法中如果sign中的值是null,也就代標鎖被釋放了,否則鎖被其他線程占用,需要通過循環(huán)來等待。在unlock方法中,通過將sign中的值設置為null來通知正在等待的線程鎖已經(jīng)被釋放。

鎖粗化

鎖粗化的概念應該比較好理解,就是將多次連接在一起的加鎖、解鎖操作合并為一次,將多個連續(xù)的鎖擴展成一個范圍更大的鎖。

public class StringBufferTest {
    StringBuffer sb = new StringBuffer();
    public void append(){
        sb.append("a");
        sb.append("b");
        sb.append("c");
    }
}
復制代碼

這里每次調用 sb.append() 方法都需要加鎖和解鎖,如果虛擬機檢測到有一系列連串的對同一個對象加鎖和解鎖操作,就會將其合并成一次范圍更大的加鎖和解鎖操作,即在第一次 append()方法時進行加鎖,最后一次 append() 方法結束后進行解鎖。

輕量級鎖

輕量級鎖是用來解決重量級鎖在互斥過程中的性能消耗問題的,所謂的重量級鎖就是 sychronized 關鍵字實現(xiàn)的鎖。 synchronized 是通過對象內部的一個叫做監(jiān)視器鎖(monitor)來實現(xiàn)的。但是監(jiān)視器鎖本質又依賴于底層的操作系統(tǒng)的 Mutex Lock 來實現(xiàn)的。而操作系統(tǒng)實現(xiàn)線程之間的切換就需要從用戶態(tài)轉換到核心態(tài),這個成本非常高,狀態(tài)之間的轉換需要相對比較長的時間。

首先,對象的對象頭中存在一個部分叫做 Mark word ,其中存儲了對象的運行時數(shù)據(jù),如哈希碼、GC年齡等,其中有2bit用于存儲鎖標志位。

在代碼進入同步塊的時候,如果對象鎖狀態(tài)為無鎖狀態(tài)(鎖標志位為“01”狀態(tài)),虛擬機首先將在當前線程的棧幀中建立一個名為 鎖記錄 ( Lock Record )的空間,用于存儲鎖對象目前的 Mark Word 的拷貝??截惓晒螅摂M機將使用CAS操作嘗試將對象的 Mark Word 更新為指向 Lock Record 的指針,并將 Lock Record 里的 owner 指針指向對的 Mark word 。并且將對象的 Mark Word 的鎖標志位變?yōu)?quot;00",表示該對象處于鎖定狀態(tài)。更新操作失敗了,虛擬機首先會檢查對象的 Mark Word 是否指向當前線程的棧幀,如果是就說明當前線程已經(jīng)擁有了這個對象的鎖,那就可以直接進入同步塊繼續(xù)執(zhí)行。否則說明多個線程競爭鎖,輕量級鎖就要膨脹為重量級鎖,鎖標志的變?yōu)椤?0”, Mark Word 中存儲的就是指向重量級鎖(互斥量)的指針,后面等待鎖的線程也要進入阻塞狀態(tài)。 而當前線程便嘗試使用自旋來獲取鎖,自旋就是為了不讓線程阻塞,而采用循環(huán)去獲取鎖的過程。

從上面我們可以看出,實際上當一個線程獲取了一個對象的輕量級鎖之后,對象的 Mark Word會指向線程的棧幀中的 Lock Record ,而棧幀中的 Lock Record 也會指向對象的 Mark Word 。 棧幀中的 Lock Record 用于判斷當前線程已經(jīng)持有了哪些對象的鎖,而對象的 Mark Word 用來判斷哪個線程持有了當前對象的鎖。 當一個線程嘗試去獲取一個對象的鎖的時候,會先通過鎖標志位判斷當前對象是否被加鎖,然后通過CAS操作來判斷當前獲取該對象鎖的線程是否是當前線程。

輕量級鎖不是設計用來取代重量級鎖的,因為它除了加鎖之外還增加了額外的CAS操作,因此在競爭激烈的情況下,輕量級鎖會比傳統(tǒng)的重量級鎖更慢。

偏向鎖

一個對象剛開始實例化的時候,沒有任何線程來訪問它的時候。它是可偏向的,意味著,它現(xiàn)在認為只可能有一個線程來訪問它,所以當?shù)谝粋€線程來訪問它的時候,它會偏向這個線程。此時,對象持有偏向鎖,偏向第一個線程。這個線程在修改對象頭成為偏向鎖的時候使用CAS操作,并將對象頭中的ThreadID改成自己的ID,之后再次訪問這個對象時,只需要對比ID,不需要再使用CAS在進行操作。

一旦有第二個線程訪問這個對象,因為偏向鎖不會主動釋放,所以第二個線程可以看到對象時偏向狀態(tài),這時表明在這個對象上已經(jīng)存在競爭了,檢查原來持有該對象鎖的線程是否依然存活,如果掛了,則可以將對象變?yōu)闊o鎖狀態(tài),然后重新偏向新的線程,如果原來的線程依然存活,則馬上執(zhí)行那個線程的操作棧,檢查該對象的使用情況,如果仍然需要持有偏向鎖,則偏向鎖升級為輕量級鎖,(偏向鎖就是這個時候升級為輕量級鎖的)。如果不存在使用了,則可以將對象回復成無鎖狀態(tài),然后重新偏向。

輕量級鎖認為競爭存在,但是競爭的程度很輕,一般兩個線程對于同一個鎖的操作都會錯開,或者說稍微等待一下(自旋),另一個線程就會釋放鎖。 但是當自旋超過一定的次數(shù),或者一個線程在持有鎖,一個在自旋,又有第三個來訪時,輕量級鎖膨脹為重量級鎖,重量級鎖使除了擁有鎖的線程以外的線程都阻塞,防止CPU空轉。

如果大多數(shù)情況下鎖總是被多個不同的線程訪問,那么偏向模式就是多余的,可以通過 -XX:-UserBiaseLocking 禁止偏向鎖優(yōu)化。

輕量級鎖和偏向鎖的提出是基于一個事實,就是大部分情況下獲取一個對象鎖的線程都是同一個線程,它在這種情形下的效率會比重量級鎖高,當鎖總是被多個不同的線程訪問它們的效率就不一定比重量級鎖高。 因此,它們的提出不是用來取代重量級鎖的,但在一些場景中會比重量級鎖效率高,因此我們可以根據(jù)自己應用的場景通過虛擬機參數(shù)來設置是否啟用它們。

感謝各位的閱讀,以上就是“JVM虛擬機內存模型與高效并發(fā)知識點有哪些”的內容了,經(jīng)過本文的學習后,相信大家對JVM虛擬機內存模型與高效并發(fā)知識點有哪些這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

jvm
AI