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

溫馨提示×

溫馨提示×

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

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

java高并發系列 - 第4天:JMM相關的一些概念

發布時間:2020-08-11 04:34:09 來源:網絡 閱讀:166 作者:路人甲Java 欄目:編程語言

JMM(java內存模型),由于并發程序要比串行程序復雜很多,其中一個重要原因是并發程序中數據訪問一致性安全性將會受到嚴重挑戰。如何保證一個線程可以看到正確的數據呢?這個問題看起來很白癡。對于串行程序來說,根本就是小菜一碟,如果你讀取一個變量,這個變量的值是1,那么你讀取到的一定是1,就是這么簡單的問題在并行程序中居然變得復雜起來。事實上,如果不加控制地任由線程胡亂并行,即使原本是1的數值,你也可能讀到2。因此我們需要在深入了解并行機制的前提下,再定義一種規則,保證多個線程間可以有小弟,正確地協同工作。而JMM也就是為此而生的。

JMM關鍵技術點都是圍繞著多線程的原子性、可見性、有序性來建立的。我們需要先了解這些概念。

原子性

原子性是指操作是不可分的,要么全部一起執行,要么不執行。在java中,其表現在對于共享變量的某些操作,是不可分的,必須連續的完成。比如a++,對于共享變量a的操作,實際上會執行3個步驟:

1.讀取變量a的值,假如a=1
2.a的值+1,為2
3.將2值賦值給變量a,此時a的值應該為2

這三個操作中任意一個操作,a的值如果被其他線程篡改了,那么都會出現我們不希望出現的結果。所以必須保證這3個操作是原子性的,在操作a++的過程中,其他線程不會改變a的值,如果在上面的過程中出現其他線程修改了a的值,在滿足原子性的原則下,上面的操作應該失敗。

java中實現原子操作的方法大致有2種:鎖機制無鎖CAS機制,后面的章節中會有介紹。

可見性

可見性是指一個線程對共享變量的修改,對于另一個線程來說是否是可以看到的。有些同學會說修改同一個變量,那肯定是可以看到的,難道線程眼盲了?

為什么會出現這種問題呢?

看一下java線程內存模型:

java高并發系列 - 第4天:JMM相關的一些概念

  • 我們定義的所有變量都儲存在主內存
  • 每個線程都有自己獨立的工作內存,里面保存該線程使用到的變量的副本(主內存中該變量的一份拷貝)
  • 線程對共享變量所有的操作都必須在自己的工作內存中進行,不能直接從主內存中讀寫(不能越級)
  • 不同線程之間也無法直接訪問其他線程的工作內存中的變量,線程間變量值的傳遞需要通過主內存來進行。(同級不能相互訪問)

線程需要修改一個共享變量X,需要先把X從主內存復制一份到線程的工作內存,在自己的工作內存中修改完畢之后,再從工作內存中回寫到主內存。
如果線程對變量的操作沒有刷寫回主內存的話,僅僅改變了自己的工作內存的變量的副本,那么對于其他線程來說是不可見的。
而如果另一個變量沒有讀取主內存中的新的值,而是使用舊的值的話,同樣的也可以列為不可見。

共享變量可見性的實現原理:

線程A對共享變量的修改要被線程B及時看到的話,需要進過以下步驟:

1.線程A在自己的工作內存中修改變量之后,需要將變量的值刷新到主內存中
2.線程B要把主內存中變量的值更新到工作內存中

關于線程可見性的控制,可以使用volatilesynchronized來實現,后面章節會有詳細介紹。

有序性

有序性指的是程序按照代碼的先后順序執行。

為了性能優化,編譯器和處理器會進行指令沖排序,有時候會改變程序語句的先后順序,比如程序。

int a = 1;  //1
int b = 20; //2
int c = a + b; //3

編譯器優化后可能變成

int b = 20;  //1
int a = 1; //2
int c = a + b; //3

上面這個例子中,編譯器調整了語句的順序,但是不影響程序的最終結果。

在單例模式的實現上有一種雙重檢驗鎖定的方式,代碼如下:

public class Singleton {
  static Singleton instance;
  static Singleton getInstance(){
    if (instance == null) {
      synchronized(Singleton.class) {
        if (instance == null)
          instance = new Singleton();
        }
    }
    return instance;
  }
}

我們先看instance = new Singleton();

未被編譯器優化的操作:

  1. 指令1:分配一款內存M
  2. 指令2:在內存M上初始化Singleton對象
  3. 指令3:將M的地址賦值給instance變量

編譯器優化后的操作指令:

  1. 指令1:分配一塊內存S
  2. 指令2:將M的地址賦值給instance變量
  3. 指令3:在內存M上初始化Singleton對象

現在有2個線程,剛好執行的代碼被編譯器優化過,過程如下:

java高并發系列 - 第4天:JMM相關的一些概念

最終線程B獲取的instance是沒有初始化的,此時去使用instance可能會產生一些意想不到的錯誤。

現在比較好的做法就是采用靜態內部內的方式實現:

public class SingletonDemo {
    private SingletonDemo() {
    }
    private static class SingletonDemoHandler{
        private static SingletonDemo instance = new SingletonDemo();
    }
    public static SingletonDemo getInstance() {
        return SingletonDemoHandler.instance;
    }
}

java高并發系列連載中,總計估計會有四五十篇文章,可以關注公眾號:javacode2018,獲取最新文章。

java高并發系列 - 第4天:JMM相關的一些概念

向AI問一下細節

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

AI

梧州市| 环江| 阜康市| 临海市| 招远市| 南溪县| 深水埗区| 赤城县| 普兰店市| 马鞍山市| 巢湖市| 安阳县| 龙海市| 大洼县| 杭锦后旗| 石渠县| 上思县| 扎鲁特旗| 和田市| 永新县| 丰都县| 东丰县| 宁津县| 榆中县| 台前县| 布拖县| 合作市| 天峻县| 宁津县| 绩溪县| 承德县| 石景山区| 密云县| 无极县| 明光市| 广丰县| 克什克腾旗| 常山县| 寻甸| 鹰潭市| 麟游县|