更新時間:2023年04月25日09時33分 來源:傳智教育 瀏覽次數(shù):
在計(jì)算機(jī)程序中,當(dāng)多個線程同時訪問同一個變量時,可能會發(fā)生線程安全問題,其中之一是變量的可見性問題。這意味著一個線程在修改了一個變量的值之后,其他線程無法立即感知到這個變化,導(dǎo)致程序出現(xiàn)不一致的行為。
為了解決這個問題,Java提供了一個關(guān)鍵字volatile。使用volatile關(guān)鍵字聲明的變量,其特點(diǎn)如下:
1.可見性:對于一個volatile變量的寫操作,JVM會立即把修改后的值刷新回主內(nèi)存中,而不是僅僅保留在本地緩存中。這樣,其他線程就能夠看到該變量的最新值,從而避免了可見性問題。
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接口等。
北京校區(qū)