久久久精品一区ed2k-女人被男人叉到高潮的视频-中文字幕乱码一区久久麻豆樱花-俄罗斯熟妇真实视频

怎么深入理解Java內(nèi)存模型JMM

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)怎么深入理解Java內(nèi)存模型JMM,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

成都創(chuàng)新互聯(lián)公司專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)、巫山網(wǎng)絡(luò)推廣、小程序開發(fā)、巫山網(wǎng)絡(luò)營(yíng)銷、巫山企業(yè)策劃、巫山品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營(yíng)等,從售前售中售后,我們都將竭誠(chéng)為您服務(wù),您的肯定,是我們最大的嘉獎(jiǎng);成都創(chuàng)新互聯(lián)公司為所有大學(xué)生創(chuàng)業(yè)者提供巫山建站搭建服務(wù),24小時(shí)服務(wù)熱線:18982081108,官方網(wǎng)址:sd-ha.com

Java 內(nèi)存模型

Java 內(nèi)存模型(JMM)是一種抽象的概念,并不真實(shí)存在,它描述了一組規(guī)則或規(guī)范,通過這組規(guī)范定義了程序中各個(gè)變量(包括實(shí)例字段、靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素)的訪問方式。試圖屏蔽各種硬件和操作系統(tǒng)的內(nèi)存訪問差異,以實(shí)現(xiàn)讓 Java 程序在各種平臺(tái)下都能達(dá)到一致的內(nèi)存訪問效果。

注意JMM與JVM內(nèi)存區(qū)域劃分的區(qū)別:

  • JMM描述的是一組規(guī)則,圍繞原子性、有序性和可見性展開;

  • 相似點(diǎn):存在共享區(qū)域和私有區(qū)域

主內(nèi)存與工作內(nèi)存

處理器上的寄存器的讀寫的速度比內(nèi)存快幾個(gè)數(shù)量級(jí),為了解決這種速度矛盾,在它們之間加入了高速緩存。

加入高速緩存帶來了一個(gè)新的問題:緩存一致性。如果多個(gè)緩存共享同一塊主內(nèi)存區(qū)域,那么多個(gè)緩存的數(shù)據(jù)可能會(huì)不一致,需要一些協(xié)議來解決這個(gè)問題。

怎么深入理解Java內(nèi)存模型JMM

所有的變量都 存儲(chǔ)在主內(nèi)存中,每個(gè)線程還有自己的工作內(nèi)存 ,工作內(nèi)存存儲(chǔ)在高速緩存或者寄存器中,保存了該線程使用的變量的主內(nèi)存副本拷貝。

線程只能直接操作工作內(nèi)存中的變量,不同線程之間的變量值傳遞需要通過主內(nèi)存來完成。

怎么深入理解Java內(nèi)存模型JMM

數(shù)據(jù)存儲(chǔ)類型以及操作方式

  • 方法中的基本類型本地變量將直接存儲(chǔ)在工作內(nèi)存的棧幀結(jié)構(gòu)中;

  • 引用類型的本地變量:引用存儲(chǔ)在工作內(nèi)存,實(shí)際存儲(chǔ)在主內(nèi)存;

  • 成員變量、靜態(tài)變量、類信息均會(huì)被存儲(chǔ)在主內(nèi)存中;

  • 主內(nèi)存共享的方式是線程各拷貝一份數(shù)據(jù)到工作內(nèi)存中,操作完成后就刷新到主內(nèi)存中。

內(nèi)存間交互操作

Java 內(nèi)存模型定義了 8 個(gè)操作來完成主內(nèi)存和工作內(nèi)存的交互操作。

怎么深入理解Java內(nèi)存模型JMM

  • read:把一個(gè)變量的值從主內(nèi)存?zhèn)鬏數(shù)焦ぷ鲀?nèi)存中

  • load:在 read 之后執(zhí)行,把 read 得到的值放入工作內(nèi)存的變量副本中

  • use:把工作內(nèi)存中一個(gè)變量的值傳遞給執(zhí)行引擎

  • assign:把一個(gè)從執(zhí)行引擎接收到的值賦給工作內(nèi)存的變量

  • store:把工作內(nèi)存的一個(gè)變量的值傳送到主內(nèi)存中

  • write:在 store 之后執(zhí)行,把 store 得到的值放入主內(nèi)存的變量中

  • lock:作用于主內(nèi)存的變量

  • unlock

指令重排序的條件

  • 在單線程環(huán)境下不能改變程序的運(yùn)行結(jié)果;

  • 存在數(shù)據(jù)依賴關(guān)系的不允許重排序;

  • 無法通過Happens-before原則推到出來的,才能進(jìn)行指令的重排序。

內(nèi)存模型三大特性

1. 原子性

Java 內(nèi)存模型保證了 read、load、use、assign、store、write、lock 和 unlock 操作具有原子性,例如對(duì)一個(gè) int 類型的變量執(zhí)行 assign 賦值操作,這個(gè)操作就是原子性的。但是 Java 內(nèi)存模型允許虛擬機(jī)將沒有被 volatile 修飾的 64 位數(shù)據(jù)(long,double)的讀寫操作劃分為兩次 32 位的操作來進(jìn)行,即 load、store、read 和 write 操作可以不具備原子性。

有一個(gè)錯(cuò)誤認(rèn)識(shí)就是,int 等原子性的類型在多線程環(huán)境中不會(huì)出現(xiàn)線程安全問題。前面的線程不安全示例代碼中,cnt 屬于 int 類型變量,1000 個(gè)線程對(duì)它進(jìn)行自增操作之后,得到的值為 997 而不是 1000。

為了方便討論,將內(nèi)存間的交互操作簡(jiǎn)化為 3 個(gè):load、assign、store。

下圖演示了兩個(gè)線程同時(shí)對(duì) cnt 進(jìn)行操作,load、assign、store 這一系列操作整體上看不具備原子性,那么在 T1 修改 cnt 并且還沒有將修改后的值寫入主內(nèi)存,T2 依然可以讀入舊值??梢钥闯觯@兩個(gè)線程雖然執(zhí)行了兩次自增運(yùn)算,但是主內(nèi)存中 cnt 的值最后為 1 而不是 2。因此對(duì) int 類型讀寫操作滿足原子性只是說明 load、assign、store 這些單個(gè)操作具備原子性。

怎么深入理解Java內(nèi)存模型JMM

AtomicInteger 能保證多個(gè)線程修改的原子性。

怎么深入理解Java內(nèi)存模型JMM

使用 AtomicInteger 重寫之前線程不安全的代碼之后得到以下線程安全實(shí)現(xiàn):

public class AtomicExample {    private AtomicInteger cnt = new AtomicInteger();    public void add() {
        cnt.incrementAndGet();
    }    public int get() {        return cnt.get();
    }
}復(fù)制代碼
public static void main(String[] args) throws InterruptedException {
    final int threadSize = 1000;
    AtomicExample example = new AtomicExample(); // 只修改這條語(yǔ)句
    final CountDownLatch countDownLatch = new CountDownLatch(threadSize);
    ExecutorService executorService = Executors.newCachedThreadPool();    for (int i = 0; i < threadSize; i++) {
        executorService.execute(() -> {
            example.add();
            countDownLatch.countDown();
        });
    }
    countDownLatch.await();
    executorService.shutdown();
    System.out.println(example.get());
}復(fù)制代碼
1000復(fù)制代碼

除了使用原子類之外,也可以使用 synchronized 互斥鎖來保證操作的原子性。它對(duì)應(yīng)的內(nèi)存間交互操作為:lock 和 unlock,在虛擬機(jī)實(shí)現(xiàn)上對(duì)應(yīng)的字節(jié)碼指令為 monitorenter 和 monitorexit。

public class AtomicSynchronizedExample {    private int cnt = 0;    public synchronized void add() {
        cnt++;
    }    public synchronized int get() {        return cnt;
    }
}復(fù)制代碼
public static void main(String[] args) throws InterruptedException {
    final int threadSize = 1000;
    AtomicSynchronizedExample example = new AtomicSynchronizedExample();
    final CountDownLatch countDownLatch = new CountDownLatch(threadSize);
    ExecutorService executorService = Executors.newCachedThreadPool();    for (int i = 0; i < threadSize; i++) {
        executorService.execute(() -> {
            example.add();
            countDownLatch.countDown();
        });
    }    countDownLatch.await();    executorService.shutdown();    System.out.println(example.get());
}復(fù)制代碼
1000復(fù)制代碼

2. 可見性

可見性指當(dāng)一個(gè)線程修改了共享變量的值,其它線程能夠立即得知這個(gè)修改。Java 內(nèi)存模型是通過在變量修改后將新值同步回主內(nèi)存,在變量讀取前從主內(nèi)存刷新變量值來實(shí)現(xiàn)可見性的。JMM 內(nèi)部的實(shí)現(xiàn)通常是依賴于所謂的 內(nèi)存屏障 ,通過 禁止某些重排序 的方式,提供內(nèi)存 可見性保證 ,也就是實(shí)現(xiàn)了 各種 happen-before 規(guī)則 。與此同時(shí),更多復(fù)雜度在于,需要盡量確保各種編譯器、各種體系結(jié)構(gòu)的處理器,都能夠提供一致的行為。

主要有有三種實(shí)現(xiàn)可見性的方式:

  • volatile,會(huì) 強(qiáng)制 將該變量自己和當(dāng)時(shí)其他變量的狀態(tài)都 刷出緩存 。

  • synchronized,對(duì)一個(gè)變量執(zhí)行 unlock 操作之前,必須把變量值同步回主內(nèi)存。

  • final,被 final 關(guān)鍵字修飾的字段在構(gòu)造器中一旦初始化完成,并且沒有發(fā)生 this 逃逸(其它線程通過 this 引用訪問到初始化了一半的對(duì)象),那么其它線程就能看見 final 字段的值。

對(duì)前面的線程不安全示例中的 cnt 變量使用 volatile 修飾,不能解決線程不安全問題,因?yàn)?volatile 并不能保證操作的原子性。

3. 有序性

有序性是指:在本線程內(nèi)觀察,所有操作都是有序的。在一個(gè)線程觀察另一個(gè)線程,所有操作都是無序的,無序是因?yàn)榘l(fā)生了指令重排序。在 Java 內(nèi)存模型中,允許編譯器和處理器對(duì)指令進(jìn)行重排序,重排序過程不會(huì)影響到單線程程序的執(zhí)行,卻會(huì)影響到多線程并發(fā)執(zhí)行的正確性。

volatile 關(guān)鍵字通過添加內(nèi)存屏障的方式來禁止指令重排,即重排序時(shí)不能把后面的指令放到內(nèi)存屏障之前。

也可以通過 synchronized 來保證有序性,它保證每個(gè)時(shí)刻只有一個(gè)線程執(zhí)行同步代碼,相當(dāng)于是讓線程順序執(zhí)行同步代碼。

先行發(fā)生原則(Happen-Before)

JSR-133內(nèi)存模型使用先行發(fā)生原則在Java內(nèi)存模型中保證多線程操作 可見性 的機(jī)制,也是對(duì)早期語(yǔ)言規(guī)范中含糊的可見性概念的一個(gè)精確定義。上面提到了可以用 volatile 和 synchronized 來保證有序性。除此之外,JVM 還規(guī)定了先行發(fā)生原則,讓一個(gè)操作 無需控制 就能先于另一個(gè)操作完成。

由于 指令重排序 的存在,兩個(gè)操作之間有happen-before關(guān)系, 并不意味著前一個(gè)操作必須要在后一個(gè)操作之前執(zhí)行。 僅僅要求前一個(gè)操作的執(zhí)行結(jié)果對(duì)于后一個(gè)操作是可見的,并且前一個(gè)操作 按順序 排在第二個(gè)操作之前。

1. 單一線程原則(程序員順序規(guī)則)

Single Thread rule

在一個(gè)線程內(nèi),在程序前面的操作先行發(fā)生于后面的操作。

怎么深入理解Java內(nèi)存模型JMM

2. 管程鎖定規(guī)則(監(jiān)視器鎖規(guī)則)

Monitor Lock Rule

一個(gè) unlock(解鎖) 操作 先行發(fā)生于 后面對(duì)同一個(gè)鎖的 lock(加鎖) 操作。

怎么深入理解Java內(nèi)存模型JMM

3. volatile 變量規(guī)則

Volatile Variable Rule

對(duì)一個(gè) volatile 變量的 寫操作 先行發(fā)生于后面對(duì)這個(gè)變量的 讀操作 。

怎么深入理解Java內(nèi)存模型JMM

4. 線程啟動(dòng)規(guī)則

Thread Start Rule

Thread 對(duì)象的 start() 方法調(diào)用先行發(fā)生于此線程的每一個(gè)動(dòng)作。

怎么深入理解Java內(nèi)存模型JMM

5. 線程加入規(guī)則

Thread Join Rule

Thread 對(duì)象的結(jié)束先行發(fā)生于 join() 方法返回。

怎么深入理解Java內(nèi)存模型JMM

6. 線程中斷規(guī)則

Thread Interruption Rule

對(duì)線程 interrupt() 方法的調(diào)用先行發(fā)生于被中斷線程的代碼檢測(cè)到中斷事件的發(fā)生,可以通過 interrupted() 方法檢測(cè)到是否有中斷發(fā)生。

7. 對(duì)象終結(jié)規(guī)則

Finalizer Rule

一個(gè)對(duì)象的初始化完成(構(gòu)造函數(shù)執(zhí)行結(jié)束)先行發(fā)生于它的 finalize() 方法的開始。

8. 傳遞性

Transitivity

如果操作 A 先行發(fā)生于操作 B,操作 B 先行發(fā)生于操作 C,那么操作 A 先行發(fā)生于操作 C。

上述就是小編為大家分享的怎么深入理解Java內(nèi)存模型JMM了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

新聞標(biāo)題:怎么深入理解Java內(nèi)存模型JMM
文章源于:http://sd-ha.com/article46/gdohhg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)建站、網(wǎng)站收錄、標(biāo)簽優(yōu)化、網(wǎng)站制作、建站公司、網(wǎng)頁(yè)設(shè)計(jì)公司

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站建設(shè)