Java并發編程(一)---原子性,可見性,有序性

      網友投稿 656 2025-04-04

      摘要

      并發編程世界里,由于CPU緩存導致的可見性問題,線程切換導致的原子性問題,以及編譯器重排序導致的有序性問題是并發編程Bug的根源。

      正文

      可見性

      一個線程對共享變量的修改。另外一個線程能夠立刻看到,我們稱之為可見性。共享變量指的是存放在堆內存,由所有線程所共享的變量。比如:實例變量,靜態變量。

      如圖所示:

      共享變量V可以由線程A和線程B同時操作,線程A和B首先從各自的CPU緩存或者寄存器中讀取數值,然后由CPU的寄存器寫入內存中。

      public class LongTest { private static long atest = 0L; public void countTest() { for (int i = 0; i < 10000; i++) { atest = atest + 1; } } public static void main(String[] args) throws InterruptedException { final LongTest longTest = new LongTest(); Thread threadA = new Thread(new Runnable() { public void run() { longTest.countTest(); } }); Thread threadB = new Thread(new Runnable() { public void run() { longTest.countTest(); } }); threadA.start(); threadB.start(); threadA.join(); threadB.join(); System.out.println("*******獲得到的atest值為=" + atest); } }

      Java并發編程(一)---原子性,可見性,有序性

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      如上程序,運行之后我們會發現 atest 值永遠都不會到達20000,而是在10000-20000之間的隨機數。

      原因分析:假設線程A和線程B同時執行 atest = atest + 1;, 線程A 讀取到的原值是0,執行+1操作之后,得到新值1,同樣的,線程B也是讀取到的原值0,然后執行+1操作,得到新值1。這樣就永遠得不到結果2。

      類推的話,循環執行10000次也是同理,線程A執行+1操作時不能及時獲得線程B已經寫入的值,故導致值永遠不可能達到20000。

      原子性

      由于一條高級語句在CPU中可能會分成若干條指令來執行,每條指令執行完之后就有可能會發生線程切換。故線程切換造成的原子性問題。

      例如: count=count+1 共有三個指令

      指令一: 將count值從內存加載到到CPU的寄存器中

      指令二:在寄存器中+1操作

      指令三 :將新值寫入到內存中(由于緩存機制,寫入的可能是CPU的緩存而不是內存)

      操作系統做任務切換可以發生在任何一條CPU指令執行完,是CPU指令執行完。

      我們將一個或者多個操作在CPU執行過程中不被中斷的特性稱之為原子性。

      有序性

      編譯器重排序導致的有序性問題:

      例如:雙重加鎖中的:

      public class SingletonDemo { private static SingletonDemo instance = null; private SingletonDemo(){ } static SingletonDemo getSingletonDemo() { if (instance == null) { synchronized (SingletonDemo.class) { if (instance == null) { instance = new SingletonDemo(); //6 } } } return instance; } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      這里會有有序性問題:

      問題主要出在了 new SingletonDemo() 這一步

      因為instance = new SingletonDemo();主要有三個指令

      分配內存空間M

      在內存M上初始化對象

      然后M的地址賦值給instance變量

      正常順序是1-2-3

      但是CPU重排序之后執行順序可能變成了 1-3-2

      步驟如下:

      A首先進入synchronized,由于instance為null,所以它執行instance = new SingletonDemo();

      然后線程 A執行1->JVM先畫出了一些分配給SingletonDemo實例的空白內存,并賦值給instance

      在還沒有進行第三步(將instance引用指向內存空間)的時候,恰好發生了線程切換 ,切換到了線程B上,

      如果此時線程B也執行getSingletonDemo()方法,那么線程B 在執行第一個判斷是會發現instance!=null,所以直接返回了instance,而此時的instance是沒有初始化的。

      總結

      并發編程中主要的問題就是可見性問題, 原子性問題,有序性問題。本文介紹了這三種問題的發生原因,以及發生的場景。

      Java 任務調度

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:CRM系統:與您的數據相關
      下一篇:wps演示文檔在不顯示縮略圖,每次找的時候都要一個一個找,文檔設置默認是勾選了顯示預覽圖的
      相關文章
      亚洲黄色免费网址| 亚洲熟妇av一区二区三区| 亚洲一区二区三区乱码A| 亚洲综合无码无在线观看| 亚洲白嫩在线观看| 亚洲伊人久久大香线蕉苏妲己| 亚洲大成色www永久网站| 国产亚洲精AA在线观看SEE | 亚洲人妻av伦理| 无码亚洲成a人在线观看| 亚洲国产精品嫩草影院| 亚洲日本在线电影| 亚洲AV无码成人精品区狼人影院| 亚洲精品无码永久在线观看男男 | 亚洲一卡2卡4卡5卡6卡在线99| 亚洲欧洲中文日产| 亚洲女人18毛片水真多| 亚洲人成在线播放| 亚洲黄页网在线观看| 亚洲欧美中文日韩视频| 精品久久久久久亚洲中文字幕| 国产精品成人亚洲| 亚洲国产精品日韩| 中文字幕亚洲无线码a| 亚洲精品V欧洲精品V日韩精品| 亚洲成av人片天堂网| 亚洲第一中文字幕| 亚洲欧洲国产综合| 亚洲一日韩欧美中文字幕在线| 亚洲国产精品网站在线播放| 亚洲国产成人久久一区久久| 亚洲性在线看高清h片| 亚洲精品午夜无码电影网| 香蕉视频在线观看亚洲| 亚洲毛片一级带毛片基地| 亚洲午夜精品一区二区麻豆| 国产成人va亚洲电影| 亚洲综合熟女久久久30p| 亚洲an天堂an在线观看| 亚洲成a人片在线观看中文!!!| 国产精品亚洲自在线播放页码|