搜尋
首頁Javajava教程Java指令重排在多執行緒環境下怎麼解決

一、序言

指令重排在單執行緒環境下有利於提高程式的執行效率,不會對程式產生負面影響;在多執行緒環境下,指令重排會為程式帶來意想不到的錯誤。

二、問題復原

(一)關聯變數

下面給出一個能夠百分之百復原指令重排的例子。

public class D {
    static Integer a;
    static Boolean flag;
    
    public static void writer() {
        a = 1;
        flag = true;
    }
    
    public static void reader() {
        if (flag != null && flag) {
            System.out.println(a);
            a = 0;
            flag = false;
        }
    }
}
1、結果預測

reader方法僅在flag變數為true時向控制台列印變數a的值。

writer方法先執行變數a的賦值運算,後來執行變數flag的賦值運算。

如果依照上述分析邏輯,那麼控制台列印的結果一定全為1。

2、指令重排

假如程式碼未發生指令重排,那麼當flag變數為true時,變數a一定為1。

上述程式碼中關於變數a和變數flag在兩個方法類別都存在指令重排的情況。

public static void writer() {
    a = 1;
    flag = true;
}

透過觀察日誌輸出,發現有大量的0輸出。

writer方法內部發生指令重排時,flag變數先完成賦值,此時假如當前執行緒發生中斷,其它執行緒在呼叫reader 方法,偵測到flag變數為true,那麼便列印變數a的值。此時控制台存在超出期望值的結果。

(二)new創建物件

使用關鍵字new建立物件時,因其非原子操作,故存在指令重排,指令重排在多執行緒環境下會帶來負面影響。

public class Singleton {
    private static UserModel instance;
    
    public static UserModel getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new UserModel(2, "B");
                }
            }
        }
        return instance;
    }
}

@Data
@AllArgsConstructor
class UserModel {
    private Integer userId;
    private String userName;
}
1、解析建立過程
  • 使用關鍵字new建立一個對象,大致分為一下過程:

  • 在堆疊空間建立參考位址

  • 以類別檔案為模版在堆疊空間物件中分配記憶體

  • ##成員變數初始化

  • #使用建構函式初始化

  • 將引用值賦值給左側儲存變數

  • ##2、重新排序過程分析
針對上述範例,假設第一個執行緒進入synchronized程式碼區塊,並開始建立對象,由於重排序存在,正常的建立物件過程被打亂,可能會出現在堆疊空間建立參考位址後,將引用值賦值給左側儲存變量,隨後因CPU調度時間片耗盡而產生中斷的情況。

後續執行緒在偵測到

instance

變數不為空,則直接使用。因為單例物件並為實例化完成,直接使用會帶來意想不到的結果。 三、應對指令重排

(一)AtomicReference原子類

使用原子類將一組相關聯的變數封裝成一個對象,利用原子操作的特性,有效迴避指令重排問題。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ValueModel {
    private Integer value;
    private Boolean flag;
}

原子類別應該是解決多執行緒環境下指令重排的首選方案,不僅簡單易懂,而且執行緒間使用的非重量級互斥鎖,效率相對較高。

public class E {
    private static final AtomicReference<ValueModel> ar = new AtomicReference<>(new ValueModel());
    
    public static void writer() {
        ar.set(new ValueModel(1, true));
    }
    
    public static void reader() {
        ValueModel valueModel = ar.get();
        if (valueModel.getFlag() != null && valueModel.getFlag()) {
            System.out.println(valueModel.getValue());
            ar.set(new ValueModel(0, false));
        }
    }
}

當一組相關聯的變數發生指令重排時,使用原子操作類別是比較優的解法。

(二)volatile關鍵字
public class Singleton {
    private volatile static UserModel instance;
    
    public static UserModel getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new UserModel(2, "B");
                }
            }
        }
        return instance;
    }
}

@Data
@AllArgsConstructor
class UserModel {
    private Integer userId;
    private String userName;
}

四、指令重排的理解

1、指令重排廣泛存在

指令重排不僅限於Java程序,實際上各種編譯器都有指令重排的操作,從軟體到CPU硬體都有。指令重排是對單執行緒執行的程式的一種效能最佳化,需要明確的是,指令重排在單執行緒環境下,不會改變順序程式執行的預期結果。

2、多執行緒環境指令重排

上面討論了兩種典型多執行緒環境下指令重排,分析其帶來負面影響,並分別提供了因應方式。

    對於關聯變量,先封裝成一個對象,然後使用原子類來操作
  • 對於new對象,使用volatile關鍵字修飾目標物件即可
  • 3、synchronized鎖定與重排序無關

synchronized鎖定透過互斥鎖,有序的保證執行緒存取特定的程式碼區塊。程式碼區塊內部的程式碼正常會依照編譯器執行的策略重新排序。

儘管synchronized鎖定能夠迴避多執行緒環境下重排序帶來的不利影響,但是互斥鎖帶來的執行緒開銷相對較大,不建議使用。

synchronized 區塊裡的非原子操作依舊可能發生指令重排

以上是Java指令重排在多執行緒環境下怎麼解決的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:亿速云。如有侵權,請聯絡admin@php.cn刪除
在Java應用程序中緩解平台特定問題的策略是什麼?在Java應用程序中緩解平台特定問題的策略是什麼?May 01, 2025 am 12:20 AM

Java如何緩解平台特定的問題? Java通過JVM和標準庫來實現平台無關性。 1)使用字節碼和JVM抽像操作系統差異;2)標準庫提供跨平台API,如Paths類處理文件路徑,Charset類處理字符編碼;3)實際項目中使用配置文件和多平台測試來優化和調試。

Java的平台獨立性與微服務體系結構之間有什麼關係?Java的平台獨立性與微服務體系結構之間有什麼關係?May 01, 2025 am 12:16 AM

java'splatformentenceenhancesenhancesmicroservicesharchitecture byferingDeploymentFlexible,一致性,可伸縮性和便攜性。 1)DeploymentFlexibilityAllowsibilityAllowsOllowsOllowSorlowsOllowsOllowsOllowSeStorunonAnyPlatformwithajvM.2)penterencyCrossServAccAcrossServAcrossServiCessImplifififiesDeevelopmentandeDe

GRAALVM與Java的平台獨立目標有何關係?GRAALVM與Java的平台獨立目標有何關係?May 01, 2025 am 12:14 AM

GraalVM通過三種方式增強了Java的平台獨立性:1.跨語言互操作,允許Java與其他語言無縫互操作;2.獨立的運行時環境,通過GraalVMNativeImage將Java程序編譯成本地可執行文件;3.性能優化,Graal編譯器生成高效的機器碼,提升Java程序的性能和一致性。

您如何測試Java應用程序的平台兼容性?您如何測試Java應用程序的平台兼容性?May 01, 2025 am 12:09 AM

效率testjavaapplicationsforplatformcompatibility oftheSesteps:1)setUpautomatedTestingTestingActingAcrossMultPlatFormSusingCitoolSlikeSlikeJenkinSorgithUbactions.2)contuctualtemualtemalualTesteTESTENRETESTINGINREALHARTWARETOLEALHARDOELHARDOLEATOCATCHISSUSESUSEUSENINCIENVIRENTMENTS.3)schictcross.3)schoscross.3)

Java編譯器(Javac)在實現平台獨立性中的作用是什麼?Java編譯器(Javac)在實現平台獨立性中的作用是什麼?May 01, 2025 am 12:06 AM

Java編譯器通過將源代碼轉換為平台無關的字節碼,實現了Java的平台獨立性,使得Java程序可以在任何安裝了JVM的操作系統上運行。

在平台獨立性的平台獨立性上使用字節碼優於本機代碼的優點是什麼?在平台獨立性的平台獨立性上使用字節碼優於本機代碼的優點是什麼?Apr 30, 2025 am 12:24 AM

ByteCodeachievesPlatFormIndenceByByByByByByExecutedBoviratualMachine(VM),允許CodetorunonanyplatformwithTheApprepreprepvm.Forexample,Javabytecodecodecodecodecanrunonanydevicewithajvm

Java真的100%獨立於平台嗎?為什麼或為什麼不呢?Java真的100%獨立於平台嗎?為什麼或為什麼不呢?Apr 30, 2025 am 12:18 AM

Java不能做到100%的平台獨立性,但其平台獨立性通過JVM和字節碼實現,確保代碼在不同平台上運行。具體實現包括:1.編譯成字節碼;2.JVM的解釋執行;3.標準庫的一致性。然而,JVM實現差異、操作系統和硬件差異以及第三方庫的兼容性可能影響其平台獨立性。

Java的平台獨立性如何支持代碼可維護性?Java的平台獨立性如何支持代碼可維護性?Apr 30, 2025 am 12:15 AM

Java通過“一次編寫,到處運行”實現平台獨立性,提升代碼可維護性:1.代碼重用性高,減少重複開發;2.維護成本低,只需一處修改;3.團隊協作效率高,方便知識共享。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。