亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

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

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

怎么理解Java詭異并發中的有序性

發布時間:2021-11-05 13:53:39 來源:億速云 閱讀:136 作者:iii 欄目:web開發

這篇文章主要介紹“怎么理解Java詭異并發中的有序性”,在日常操作中,相信很多人在怎么理解Java詭異并發中的有序性問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么理解Java詭異并發中的有序性”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

序、有序性的闡述

有序性為什么要探討?因為 Java  是面向對象編程的,關注的只是最終結果,很少去研究其具體執行過程?正如上一篇文章在介紹可見性時描述的一樣,操作系統為了提升性能,將 Java  語言轉換成機器語言的時候,吩咐編譯器對語句的執行順序進行了一定的修改,以促使系統性能達到最優。所以在很多情況下,訪問一個程序變量(對象實例字段,類靜態字段和數組元素)可能會使用不同的順序執行,而不是程序語義所指定的順序執行。

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

而編譯器的這種修改是自以為能保證最終運行結果!因為在單核時代完全沒問題;但是隨著多核時代的到來,多線程的環境下,這種優化碰上線程切換就大大的增加了事故的出現幾率!

好心辦了壞事!

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

有序性問題  指的是在多線程環境下(多核),由于執行語句重排序后,重排序的這一部分沒有一起執行完,就切換到了其它線程,導致的結果與預期不符的問題。這就是編譯器的編譯優化給并發編程帶來的程序有序性問題。

用圖示就是:

怎么理解Java詭異并發中的有序性

阿粉小結:編譯優化最終導致了有序性問題。

一、導致有序性的原因:

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

例子:

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

運行結果:

怎么理解Java詭異并發中的有序性

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

用圖示,則為:

怎么理解Java詭異并發中的有序性

二、如何解決有序性

2.1、volatile

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

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

怎么理解Java詭異并發中的有序性

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

1    private static volatile boolean flag = false;

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

怎么理解Java詭異并發中的有序性

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

怎么理解Java詭異并發中的有序性

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

怎么理解Java詭異并發中的有序性

2.2、加鎖

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

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

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

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

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

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

到此,關于“怎么理解Java詭異并發中的有序性”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

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

AI

天祝| 宁陵县| 墨玉县| 灌南县| 江源县| 大洼县| 巧家县| 仁化县| 娱乐| 隆子县| 新沂市| 乌拉特中旗| 伊宁市| 衡山县| 册亨县| 辽宁省| 瑞昌市| 南宁市| 什邡市| 镇安县| 中宁县| 宜君县| 轮台县| 民丰县| 山丹县| 名山县| 孝感市| 来安县| 武川县| 久治县| 茶陵县| 太原市| 长阳| 焦作市| 尼勒克县| 新宾| 绍兴市| 方山县| 鲁山县| 吴桥县| 都昌县|