教育行業(yè)A股IPO第一股(股票代碼 003032)

全國咨詢/投訴熱線:400-618-4000

為什么volatile能保證變量對所有線程的可見性?

更新時間:2023年04月25日09時33分 來源:傳智教育 瀏覽次數(shù):

好口碑IT培訓(xùn)

  在計(jì)算機(jī)程序中,當(dāng)多個線程同時訪問同一個變量時,可能會發(fā)生線程安全問題,其中之一是變量的可見性問題。這意味著一個線程在修改了一個變量的值之后,其他線程無法立即感知到這個變化,導(dǎo)致程序出現(xiàn)不一致的行為。

  為了解決這個問題,Java提供了一個關(guān)鍵字volatile。使用volatile關(guān)鍵字聲明的變量,其特點(diǎn)如下:

  1.可見性:對于一個volatile變量的寫操作,JVM會立即把修改后的值刷新回主內(nèi)存中,而不是僅僅保留在本地緩存中。這樣,其他線程就能夠看到該變量的最新值,從而避免了可見性問題。

為什么volatile能保證變量對所有線程的可見性?

  2.禁止指令重排:volatile關(guān)鍵字還可以禁止JVM對指令的重排優(yōu)化。在不加volatile關(guān)鍵字的情況下,JVM為了提高性能,可能會對指令進(jìn)行重排,導(dǎo)致程序出現(xiàn)意外的行為。而使用volatile關(guān)鍵字聲明的變量,JVM會保證指令的執(zhí)行順序和程序的代碼順序一致,從而避免了這種問題。

  接下來,我們通過一段代碼來演示下volatile關(guān)鍵字如何保證可見性:

public class VolatileDemo {
    private volatile boolean flag = false;

    public void setFlag() {
        flag = true;
    }

    public void printFlag() {
        System.out.println("flag = " + flag);
    }

    public static void main(String[] args) {
        final VolatileDemo demo = new VolatileDemo();

        new Thread(() -> {
            try {
                Thread.sleep(1000); // 模擬線程1執(zhí)行時間
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            demo.setFlag();
        }).start();

        new Thread(() -> {
            while (!demo.flag) { // 如果flag不是volatile,該循環(huán)可能會一直執(zhí)行下去
                // do nothing
            }
            demo.printFlag();
        }).start();
    }
}

  在這個代碼中,我們聲明了一個名為flag的布爾型volatile變量。在程序啟動后,我們啟動了兩個線程,線程1會在1秒后將flag的值設(shè)為true,線程2會在flag變?yōu)閠rue之前一直循環(huán)等待。由于flag是volatile類型,線程2能夠正確感知到flag的變化,從而在flag變?yōu)閠rue后打印出相應(yīng)的信息。如果flag不是volatile類型,線程2可能會一直等待下去,因?yàn)樗鼰o法感知到flag的變化,從而導(dǎo)致程序出現(xiàn)錯誤的結(jié)果。

  需要注意的是,volatile關(guān)鍵字雖然可以保證可見性和禁止指令重排,但并不能保證原子性。也就是說,如果一個變量被多個線程同時訪問,并且這些線程都對它進(jìn)行修改操作,那么使用volatile關(guān)鍵字并不能保證程序的正確性。這時候需要使用其他的線程同步機(jī)制,如synchronized關(guān)鍵字或者Lock接口等。

0 分享到:
和我們在線交談!