首頁常見問題正文

volatile能使得一個非原子操作變成原子操作嗎?

更新時間:2023-07-26 來源:黑馬程序員 瀏覽量:

IT培訓(xùn)班

  在Java中,volatile關(guān)鍵字可以用于修飾變量,用于保證可見性和防止指令重排序。但是,volatile不能將一個非原子操作變成原子操作。

  原子操作是指在執(zhí)行過程中不會被中斷的操作,要么完全執(zhí)行,要么完全不執(zhí)行,不會出現(xiàn)中間狀態(tài)。volatile關(guān)鍵字只保證了可見性,即當(dāng)一個線程修改了volatile變量的值后,其他線程能夠立即看到該變量的最新值,而不是使用緩存的舊值。

  然而,如果涉及到多步驟的操作,volatile并不能保證這些操作的原子性。在多線程環(huán)境下,可能會出現(xiàn)線程間的競態(tài)條件和不一致的結(jié)果。

  下面,我們通過一個簡單的示例來演示volatile不能將非原子操作變成原子操作:

public class VolatileExample {
    private volatile int counter = 0;

    public void increment() {
        counter++; // 非原子操作,涉及讀取、修改、寫入三個步驟
    }

    public int getCounter() {
        return counter;
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        final int THREAD_COUNT = 1000;
        VolatileExample volatileExample = new VolatileExample();

        List<Thread> threads = new ArrayList<>();
        for (int i = 0; i < THREAD_COUNT; i++) {
            threads.add(new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    volatileExample.increment();
                }
            }));
        }

        for (Thread thread : threads) {
            thread.start();
        }

        for (Thread thread : threads) {
            thread.join();
        }

        System.out.println("Final Counter Value: " + volatileExample.getCounter());
    }
}

  在上面的例子中,我們創(chuàng)建了1000個線程,并讓每個線程執(zhí)行1000次對counter的增加操作。由于counter++是一個非原子操作,即使counter被聲明為volatile,最終得到的結(jié)果也可能不是我們期望的1000 * 1000 = 1000000。因為多個線程可能同時讀取相同的counter值,然后進行遞增并寫回,導(dǎo)致部分遞增操作被覆蓋。

  要保證多個線程對counter的遞增操作是原子的,可以使用Java提供的原子類,如AtomicInteger:

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicExample {
    private AtomicInteger counter = new AtomicInteger(0);

    public void increment() {
        counter.incrementAndGet(); // 使用原子類保證原子遞增操作
    }

    public int getCounter() {
        return counter.get();
    }
}

  使用AtomicInteger可以確保遞增操作的原子性,從而得到正確的結(jié)果。

  總結(jié)起來,volatile關(guān)鍵字不能將非原子操作變成原子操作。它只能保證變量的可見性,但無法解決多線程環(huán)境下的競態(tài)條件問題。要保證原子操作,可以使用Java提供的原子類或者使用synchronized關(guān)鍵字來實現(xiàn)同步。

分享到:
在線咨詢 我要報名
和我們在線交談!