JVM synchronized和volatile 關鍵字

本編文章都是基于下圖這個,計算機cpu 、緩存、內存、線程之間的關系;


無標題.png

一、緩存一致性問題

計算機在執行程序時,每條指令都是在CPU中執行的,而執行指令過程中,勢必涉及到數據的讀取和寫入。由于程序運行過程中的臨時數據是存放在主存(物理內存)當中的,這時就存在一個問題,由于CPU執行速度很快,而從內存讀取數據和向內存寫入數據的過程跟CPU執行指令的速度比起來要慢的多,因此如果任何時候對數據的操作都要通過和內存的交互來進行,會大大降低指令執行的速度。因此在CPU里面就有了高速緩存。

當程序在運行過程中,會將運算需要的數據從主存復制一份到CPU的高速緩存當中,那么CPU進行計算時就可以直接從它的高速緩存讀取數據和向其中寫入數據,當運算結束之后,再將高速緩存中的數據刷新到主存當中。

中間的高速緩存就是cpu和內存的中間過程。
但是在多線程,每個線程在不同的cpu中運行時,每個線程分別讀取內存中的值存入各自所在的CPU的高速緩存當中,cpu對數據改變后,就造成了緩存一致性的問題,通常稱這種被多個線程訪問的變量為共享變量。

也就是說,如果一個變量在多個CPU中都存在緩存(一般在多線程編程時才會出現),那么就可能存在緩存不一致的問題。

為了解決緩存不一致性問題,通常來說有以下2種解決方法:

1)通過synchronized鎖的方式
2)通過緩存一致性協議

二、并發編程中的三個概念

  • 原子性:即一個操作或者多個操作 要么全部執行并且執行的過程不會被任何因素打斷,要么就都不執行,相當于事物的概念。

  • 可見性:可見性是指當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其他線程能夠立即看得到修改的值。

  • 有序性:即程序執行的順序按照代碼的先后順序執行,因為有指令重排序問題;
    指令重排序,一般來說,處理器為了提高程序運行效率,可能會對輸入代碼進行優化,它不保證程序中各個語句的執行先后順序同代碼中的順序一致,但是它會保證程序最終執行結果和代碼順序執行的結果是一致的。這種情況在單線程下沒有問題,但是在多線程下有可能出現問題。

也就是說,要想并發程序正確地執行,必須要保證原子性、可見性以及有序性。只要有一個沒有被保證,就有可能會導致程序運行不正確。

三、synchronized

同步鎖可以保證并發編程中的原子性、可見性、有序性;但是效率比較低。

四、volatile

  1. 保證可見性問題:
    一個共享變量被volatile修飾時,當CPU對該變量有寫操作時,它會保證修改的值會立即被更新到主內存中,并會發出信號通知其他CPU將該變量的緩存設置為無效狀態,因此當其他CPU需要讀取這個變量時,發現自己的高速緩存中該變量是無效的,那么它就會從內存重新讀取。

    而普通的共享變量不能保證可見性,因為普通共享變量被修改之后,什么時候被寫入主存是不確定的,當其他線程去讀取時,此時內存中可能還是原來的舊值,因此無法保證可見性。

    可見性只能保證每次讀取的是最新的值

  1. 保證有序性問題:
    當程序執行到volatile變量的讀操作或者寫操作時,在其前面的操作的更改肯定全部已經進行,且結果已經對后面的操作可見;在其后面的操作肯定還沒有進行;

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

  2. 不能保證原子性問題:
    測試代碼:

package com.test.jvm;

public class Test {
  
  public volatile int i = 0;  
  public void increase(){   //可以添加關鍵字synchronized看結果不同
      i++;
  }
  
  public static void main(String[] args) {        
      final Test test = new Test();
      for(int x =0; x<10; x++){
          new Thread(){@Override
              public void run() {
                  for(int y=0; y<1000; y++){
                      test.increase();
                  }
              }
          }.start();
      }
          
      while(Thread.activeCount()>1){  //保證前面的線程都執行完
          Thread.yield();
          System.out.println(test.i);
       }      
  }

}

最后i的結果并不是10000 ,總是小于10000;
解釋:
假如某個時刻變量 i 的值為10,

cpu1中線程A對變量進行自增操作,線程A先讀取了變量 i 的原始值,然后線程A被阻塞了;

然后cpu2中線程B對變量進行自增操作,線程B也去讀取變量 i 的原始值,由于線程A只是對變量 i 進行讀取操作,而沒有對變量進行修改操作,所以不會導致線程B的工作內存中緩存變量 i 的緩存無效,所以線程B中 i 的值10,然后進行加1操作,并把11寫入工作內存,最后寫入主存。

然后線程A接著進行加1操作,由于已經讀取了 i 的值,注意此時在線程A的工作內存中 i
的值仍然為10,所以線程A對 i 進行加1操作后 i 的值為11,然后將11寫入工作內存,最后寫入主存。

那么兩個線程分別進行了一次自增操作后,i 只增加了1。

解釋到這里,可能有朋友會有疑問,不對啊,前面不是保證一個變量在修改volatile變量時,會讓緩存行無效嗎?然后其他線程去讀就會讀到新的值,對,這個沒錯。但是要注意,線程A對變量進行讀取操作之后,被阻塞了的話,并沒有對 i 值進行修改。然后雖然volatile能保證線程B對變量 i 的值讀取是從內存中讀取的,但是線程A沒有進行修改,所以線程B根本就不會看到修改的值。

總結:

使用volatitle關鍵字要保證的兩個條件:
1) 對變量的寫操作不依賴于當前值
2) 該變量沒有包含在其他變量中

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內容