中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

JMM定義了什么

發布時間:2022-01-07 17:00:33 來源:億速云 閱讀:154 作者:iii 欄目:云計算

今天小編給大家分享一下JMM定義了什么的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

JMM就是Java內存模型(java memory model)。因為在不同的硬件生產商和不同的操作系統下,內存的訪問有一定的差異,所以會造成相同的代碼運行在不同的系統上會出現各種問題。所以java內存模型(JMM)屏蔽掉各種硬件和操作系統的內存訪問差異,以實現讓java程序在各種平臺下都能達到一致的并發效果。

Java內存模型規定所有的變量都存儲在主內存中,包括實例變量,靜態變量,但是不包括局部變量和方法參數。每個線程都有自己的工作內存,線程的工作內存保存了該線程用到的變量和主內存的副本拷貝,線程對變量的操作都在工作內存中進行線程不能直接讀寫主內存中的變量

不同的線程之間也無法訪問對方工作內存中的變量。線程之間變量值的傳遞均需要通過主內存來完成。

如果聽起來抽象的話,我可以畫張圖給你看看,會直觀一點:

JMM定義了什么

每個線程的工作內存都是獨立的,線程操作數據只能在工作內存中進行,然后刷回到主存。這是 Java 內存模型定義的線程基本工作方式。

溫馨提醒一下,這里有些人會把Java內存模型誤解為Java內存結構,然后答到堆,棧,GC垃圾回收,最后和面試官想問的問題相差甚遠。實際上一般問到Java內存模型都是想問多線程,Java并發相關的問題

面試官:那JMM定義了什么

這個簡單,整個Java內存模型實際上是圍繞著三個特征建立起來的。分別是:原子性,可見性,有序性。這三個特征可謂是整個Java并發的基礎。

原子性

原子性指的是一個操作是不可分割,不可中斷的,一個線程在執行時不會被其他線程干擾。

面試官拿筆寫了段代碼,下面這幾句代碼能保證原子性嗎

int i = 2;int j = i;
i++;
i = i + 1;

第一句是基本類型賦值操作,必定是原子性操作。

第二句先讀取i的值,再賦值到j,兩步操作,不能保證原子性。

第三和第四句其實是等效的,先讀取i的值,再+1,最后賦值到i,三步操作了,不能保證原子性。

JMM只能保證基本的原子性,如果要保證一個代碼塊的原子性,提供了monitorenter 和 moniterexit 兩個字節碼指令,也就是 synchronized 關鍵字。因此在 synchronized 塊之間的操作都是原子性的。

可見性

可見性指當一個線程修改共享變量的值,其他線程能夠立即知道被修改了。Java是利用volatile關鍵字來提供可見性的。 當變量被volatile修飾時,這個變量被修改后會立刻刷新到主內存,當其它線程需要讀取該變量時,會去主內存中讀取新值。而普通變量則不能保證這一點。

除了volatile關鍵字之外,final和synchronized也能實現可見性。

synchronized的原理是,在執行完,進入unlock之前,必須將共享變量同步到主內存中。

final修飾的字段,一旦初始化完成,如果沒有對象逸出(指對象為初始化完成就可以被別的線程使用),那么對于其他線程都是可見的。

有序性

在Java中,可以使用synchronized或者volatile保證多線程之間操作的有序性。實現原理有些區別:

volatile關鍵字是使用內存屏障達到禁止指令重排序,以保證有序性。

synchronized的原理是,一個線程lock之后,必須unlock后,其他線程才可以重新lock,使得被synchronized包住的代碼塊在多線程之間是串行執行的。

面試官:給我講一下八種內存交互操作吧

好的,面試官,內存交互操作有8種,我畫張圖給你看吧:

JMM定義了什么

volatile一定能保證線程安全嗎

先說結論吧,volatile不能一定能保證線程安全。

怎么證明呢,我們看下面一段代碼的運行結果就知道了:

/**
 * @author Ye Hongzhi 公眾號:java技術愛好者
 **/public class VolatileTest extends Thread {private static volatile int count = 0;public static void main(String[] args) throws Exception {
        Vector<Thread> threads = new Vector<>();for (int i = 0; i < 100; i++) {
            VolatileTest thread = new VolatileTest();
            threads.add(thread);
            thread.start();
        }//等待子線程全部完成for (Thread thread : threads) {
            thread.join();
        }//輸出結果,正確結果應該是1000,實際卻是984System.out.println(count);//984}@Overridepublic void run() {for (int i = 0; i < 10; i++) {try {//休眠500毫秒Thread.sleep(500);
            } catch (Exception e) {
                e.printStackTrace();
            }
            count++;
        }
    }
}

為什么volatile不能保證線程安全?

很簡單呀,可見性不能保證操作的原子性,前面說過了count++不是原子性操作,會當做三步,先讀取count的值,然后+1,最后賦值回去count變量。需要保證線程安全的話,需要使用synchronized關鍵字或者lock鎖,給count++這段代碼上鎖:

private static synchronized void add() {
    count++;
}

禁止指令重排序

首先要講一下as-if-serial語義,不管怎么重排序,(單線程)程序的執行結果不能被改變。

為了使指令更加符合CPU的執行特性,最大限度的發揮機器的性能,提高程序的執行效率,只要程序的最終結果與它順序化情況的結果相等,那么指令的執行順序可以與代碼邏輯順序不一致,這個過程就叫做指令的重排序

重排序的種類分為三種,分別是:編譯器重排序,指令級并行的重排序,內存系統重排序。整個過程如下所示:

指令重排序在單線程是沒有問題的,不會影響執行結果,而且還提高了性能。但是在多線程的環境下就不能保證一定不會影響執行結果了。

所以在多線程環境下,就需要禁止指令重排序

volatile關鍵字禁止指令重排序有兩層意思:

  • 當程序執行到volatile變量的讀操作或者寫操作時,在其前面的操作的更改肯定全部已經進行,且結果已經對后面的操作可見,在其后面的操作肯定還沒有進行。

  • 在進行指令優化時,不能將在對volatile變量訪問的語句放在其后面執行,也不能把volatile變量后面的語句放到其前面執行。

下面舉個例子:

private static int a;//非volatile修飾變量private static int b;//非volatile修飾變量private static volatile int k;//volatile修飾變量private void hello() {
    a = 1;  //語句1b = 2;  //語句2k = 3;  //語句3a = 4;  //語句4b = 5;  //語句5//以下省略...}

變量a,b是非volatile修飾的變量,k則使用volatile修飾。所以語句3不能放在語句1、2前,也不能放在語句4、5后。但是語句1、2的順序是不能保證的,同理,語句4、5也不能保證順序。

并且,執行到語句3的時候,語句1,2是肯定執行完畢的,而且語句1,2的執行結果對于語句3,4,5是可見的。

volatile禁止指令重排序的原理是什么

首先要講一下內存屏障,內存屏障可以分為以下幾類:

  • LoadLoad 屏障:對于這樣的語句Load1,LoadLoad,Load2。在Load2及后續讀取操作要讀取的數據被訪問前,保證Load1要讀取的數據被讀取完畢。

  • StoreStore屏障:對于這樣的語句Store1, StoreStore, Store2,在Store2及后續寫入操作執行前,保證Store1的寫入操作對其它處理器可見。

  • LoadStore 屏障:對于這樣的語句Load1, LoadStore,Store2,在Store2及后續寫入操作被刷出前,保證Load1要讀取的數據被讀取完畢。

  • StoreLoad 屏障:對于這樣的語句Store1, StoreLoad,Load2,在Load2及后續所有讀取操作執行前,保證Store1的寫入對所有處理器可見。

在每個volatile讀操作后插入LoadLoad屏障,在讀操作后插入LoadStore屏障。

JMM定義了什么

在每個volatile寫操作的前面插入一個StoreStore屏障,后面插入一個SotreLoad屏障。

以上就是“JMM定義了什么”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

向AI問一下細節

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

jmm
AI

武冈市| 华蓥市| 清徐县| 嘉善县| 南丰县| 定日县| 广元市| 清徐县| 梅河口市| 灌阳县| 宁城县| 即墨市| 同心县| 唐海县| 名山县| 利辛县| 临夏市| 广丰县| 察哈| 宜兴市| 咸阳市| 封丘县| 怀远县| 阿克陶县| 沙坪坝区| 临江市| 景宁| 临夏县| 临海市| 会东县| 武宣县| 伽师县| 昆山市| 耒阳市| 北京市| 望奎县| 仙游县| 南丰县| 齐河县| 望谟县| 泰和县|