成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

詭異并發(fā)三大惡人之有序性

開發(fā) 前端
正如大家所熟知那樣,Java語言是運(yùn)行在 Java 自帶的 JVM(Java Virtual Machine) 環(huán)境中,在JVM環(huán)境中源代碼(.class)的執(zhí)行順序與程序的執(zhí)行順序(runtime)不一致,或者程序執(zhí)行順序與編譯器執(zhí)行順序不一致的情況下,我們就稱程序執(zhí)行過程中發(fā)生了重排序。

[[317250]]

 上一節(jié)阿粉我和大家一起打到了并發(fā)中的惡人可見性和原子性,這一節(jié)我們繼續(xù)討伐三惡之一的有序性。

序、有序性的闡述

有序性為什么要探討?因?yàn)?Java 是面向?qū)ο缶幊痰模P(guān)注的只是最終結(jié)果,很少去研究其具體執(zhí)行過程?正如上一篇文章在介紹可見性時描述的一樣,操作系統(tǒng)為了提升性能,將 Java 語言轉(zhuǎn)換成機(jī)器語言的時候,吩咐編譯器對語句的執(zhí)行順序進(jìn)行了一定的修改,以促使系統(tǒng)性能達(dá)到最優(yōu)。所以在很多情況下,訪問一個程序變量(對象實(shí)例字段,類靜態(tài)字段和數(shù)組元素)可能會使用不同的順序執(zhí)行,而不是程序語義所指定的順序執(zhí)行。

正如大家所熟知那樣,Java語言是運(yùn)行在 Java 自帶的 JVM(Java Virtual Machine) 環(huán)境中,在JVM環(huán)境中源代碼(.class)的執(zhí)行順序與程序的執(zhí)行順序(runtime)不一致,或者程序執(zhí)行順序與編譯器執(zhí)行順序不一致的情況下,我們就稱程序執(zhí)行過程中發(fā)生了重排序。

而編譯器的這種修改是自以為能保證最終運(yùn)行結(jié)果!因?yàn)樵趩魏藭r代完全沒問題;但是隨著多核時代的到來,多線程的環(huán)境下,這種優(yōu)化碰上線程切換就大大的增加了事故的出現(xiàn)幾率!

好心辦了壞事!

 

 

 

[[317251]]

 

也就是說,有序性 指的是在代碼順序結(jié)構(gòu)中,我們可以直觀的指定代碼的執(zhí)行順序, 即從上到下按序執(zhí)行。但編譯器和CPU處理器會根據(jù)自己的決策,對代碼的執(zhí)行順序進(jìn)行重新排序。優(yōu)化指令的執(zhí)行順序,提升程序的性能和執(zhí)行速度,使語句執(zhí)行順序發(fā)生改變,出現(xiàn)重排序,但最終結(jié)果看起來沒什么變化(單核)。

有序性問題 指的是在多線程環(huán)境下(多核),由于執(zhí)行語句重排序后,重排序的這一部分沒有一起執(zhí)行完,就切換到了其它線程,導(dǎo)致的結(jié)果與預(yù)期不符的問題。這就是編譯器的編譯優(yōu)化給并發(fā)編程帶來的程序有序性問題。

用圖示就是:

 

 

 

 

阿粉小結(jié):編譯優(yōu)化最終導(dǎo)致了有序性問題。

一、導(dǎo)致有序性的原因:

如果一個線程寫入值到字段 a,然后寫入值到字段 b ,而且b的值不依賴于 a 的值,那么,處理器就能夠自由的調(diào)整它們的執(zhí)行順序,而且緩沖區(qū)能夠在 a 之前刷新b的值到主內(nèi)存。此時就可能會出現(xiàn)有序性問題。

例子:

 

  1. 1import java.time.LocalDateTime; 
  2.  2 
  3.  3/** 
  4.  4 * @author :mmzsblog 
  5.  5 * @description:并發(fā)中的有序性問題 
  6.  6 * @date :2020年2月26日 15:22:05 
  7.  7 */ 
  8.  8public class OrderlyDemo { 
  9.  9 
  10. 10    static int value = 1; 
  11. 11    private static boolean flag = false
  12. 12 
  13. 13    public static void main(String[] args) throws InterruptedException { 
  14. 14        for (int i = 0; i < 199; i++) { 
  15. 15            value = 1; 
  16. 16            flag = false
  17. 17            Thread thread1 = new DisplayThread(); 
  18. 18            Thread thread2 = new CountThread(); 
  19. 19            thread1.start(); 
  20. 20            thread2.start(); 
  21. 21            System.out.println("========================================================="); 
  22. 22            Thread.sleep(6000); 
  23. 23        } 
  24. 24    } 
  25. 25 
  26. 26    static class DisplayThread extends Thread { 
  27. 27        @Override 
  28. 28        public void run() { 
  29. 29            System.out.println(Thread.currentThread().getName() + " DisplayThread begin, time:" + LocalDateTime.now()); 
  30. 30            value = 1024; 
  31. 31            System.out.println(Thread.currentThread().getName() + " change flag, time:" + LocalDateTime.now()); 
  32. 32            flag = true
  33. 33            System.out.println(Thread.currentThread().getName() + " DisplayThread end, time:" + LocalDateTime.now()); 
  34. 34        } 
  35. 35    } 
  36. 36 
  37. 37    static class CountThread extends Thread { 
  38. 38        @Override 
  39. 39        public void run() { 
  40. 40            if (flag) { 
  41. 41                System.out.println(Thread.currentThread().getName() + " value的值是:" + value + ", time:" + LocalDateTime.now()); 
  42. 42                System.out.println(Thread.currentThread().getName() + " CountThread flag is true,  time:" + LocalDateTime.now()); 
  43. 43            } else { 
  44. 44                System.out.println(Thread.currentThread().getName() + " value的值是:" + value + ", time:" + LocalDateTime.now()); 
  45. 45                System.out.println(Thread.currentThread().getName() + " CountThread flag is false, time:" + LocalDateTime.now()); 
  46. 46            } 
  47. 47        } 
  48. 48    } 
  49. 49} 

運(yùn)行結(jié)果:

 

 

 

 

從打印的可以看出:在 DisplayThread 線程執(zhí)行的時候肯定是發(fā)生了重排序,導(dǎo)致先為 flag 賦值,然后切換到 CountThread 線程,這才出現(xiàn)了打印的 value 值是1,falg 值是 true 的情況,再為 value 賦值;不過出現(xiàn)這種情況的原因就是這兩個賦值語句之間沒有聯(lián)系,所以編譯器在進(jìn)行代碼編譯的時候就可能進(jìn)行指令重排序。

用圖示,則為:

 

 

 

 

二、如何解決有序性

2.1、volatile

volatile 的底層是使用內(nèi)存屏障來保證有序性的(讓一個 CPU 緩存中的狀態(tài)(變量)對其他 CPU 緩存可見的一種技術(shù))。

volatile 變量有條規(guī)則是指對一個 volatile 變量的寫操作, Happens-Before于后續(xù)對這個 volatile 變量的讀操作。并且這個規(guī)則具有傳遞性,也就是說:

 

 

 

 

此時,我們定義變量 flag 時使用 volatile 關(guān)鍵字修飾,如:

 

  1. 1    private static volatile boolean flag = false

此時,變量的含義是這樣子的:

 

 

 

 

也就是說,只要讀取到 flag=true; 就能讀取到 value=1024;否則就是讀取到flag=false; 和 value=1 的還沒被修改過的初始狀態(tài);

 

 

 

 

但也有可能會出現(xiàn)線程切換帶來的原子性問題,就是讀取到 flag=false; 而value=1024 的情況;看過上一篇講述[原子性]()的文章的小伙伴,可能就立馬明白了,這是線程切換導(dǎo)致的。

 

 

 

 

2.2、加鎖

此處我們直接采用Java語言內(nèi)置的關(guān)鍵字 synchronized,為可能會重排序的部分加鎖,讓其在宏觀上或者說執(zhí)行結(jié)果上看起來沒有發(fā)生重排序。

代碼修改也很簡單,只需用 synchronized 關(guān)鍵字修飾 run 方法即可,代碼如下:

 

  1. 1    public synchronized void run() { 
  2. 2        value = 1024; 
  3. 3        flag = true
  4. 4    } 

同理,既然是加鎖,當(dāng)然也可以使用 Lock 加鎖,但 Lock 必須要用戶去手動釋放鎖,如果沒有主動釋放鎖,就有可能導(dǎo)致出現(xiàn)死鎖現(xiàn)象。這點(diǎn)在使用的時候一定要注意!

使用該種方式加鎖也很簡單,代碼如下:

 

  1. 1    readWriteLock.writeLock().lock(); 
  2. 2    try { 
  3. 3        value = 1024; 
  4. 4        flag = true
  5. 5    } finally { 
  6. 6        readWriteLock.writeLock().unlock(); 
  7. 7    } 

好了,以上內(nèi)容就是我對并發(fā)中的有序性的一點(diǎn)理解與總結(jié)了,通過這三篇文章我們也就大致掌握了并發(fā)中常見的可見性、有序性、原子性問題以及它們常見的解決方案。

最后

阿粉簡單總結(jié)下三篇文章文章中使用的解決方案之間的區(qū)別:

 

 

 

 

References

[1]: https://juejin.im/post/5d52abd1e51d4561e6237124

[2]: https://juejin.im/post/5d89fd1bf265da03e71b3605

[3]: https://www.cnblogs.com/54chensongxia/p/12120117.html

[4]: http://ifeve.com/jmm-faq-reordering/

 

責(zé)任編輯:武曉燕 來源: Java極客技術(shù)
相關(guān)推薦

2021-01-12 07:39:48

線程線程安全

2021-05-16 17:14:30

線程安全性

2024-02-27 17:46:25

并發(fā)程序CPU

2021-05-06 19:20:05

Java內(nèi)存模型

2024-11-18 16:37:35

JMMJava內(nèi)存模型

2022-12-04 09:19:25

JAVA并發(fā)有序性

2020-02-28 14:48:51

結(jié)構(gòu)系統(tǒng)程序

2016-09-19 21:53:30

Java并發(fā)編程解析volatile

2010-09-01 13:27:34

2025-02-20 18:17:41

2010-07-19 15:07:23

SQL Server評

2022-07-02 08:40:00

并發(fā)編程

2020-08-27 08:17:05

緩存高并發(fā)系統(tǒng)

2024-03-11 15:13:22

數(shù)據(jù)庫高并發(fā)

2019-08-19 15:36:55

SynchronizeVolatile性能

2018-08-07 16:01:32

synchronizevolatilefinal

2024-02-26 08:33:51

并發(fā)編程活躍性安全性

2023-12-27 06:51:21

可觀測性系統(tǒng)數(shù)字體驗(yàn)

2021-12-13 10:43:45

HashMapJava集合容器

2021-03-18 00:14:29

JavaCyclicBarri高并發(fā)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 欧美久久精品一级c片 | 欧美精品成人一区二区三区四区 | 黄色大片免费网站 | 最新日韩在线视频 | 欧美日韩国产传媒 | 国产精品美女久久久久久久网站 | 成人免费大片黄在线播放 | 亚洲综合在线一区 | 一级欧美 | 国产日韩欧美精品一区二区 | 亚洲精品中文在线 | 国产激情一区二区三区 | 成人免费看片 | 国产亚洲一区二区三区 | 亚洲激情综合网 | 99re在线视频 | 久久久国产一区二区三区 | 日韩精品一区二区三区中文字幕 | 高清一区二区三区 | 久久久新视频 | 日本不卡一区 | 免费黄色a视频 | 欧美视频一区二区三区 | 二区国产 | 四虎影视 | 51ⅴ精品国产91久久久久久 | 视频一区 国产精品 | 日日夜夜精品免费视频 | 精品国产色 | 久久丝袜 | 在线免费国产视频 | 成人午夜视频在线观看 | 欧美一级黄色片免费观看 | 国产精品一区二区在线观看 | eeuss国产一区二区三区四区 | 成人午夜在线 | 二区在线观看 | 久久久精品 | 亚洲欧美日韩在线不卡 | 伊人青青久久 | 亚洲在线电影 |