Can volatile make a non-atomic operation atomic?

  In Java, the volatile keyword can be used to modify variables to ensure visibility and prevent instruction reordering. However, volatile cannot turn a nonatomic operation into an atomic operation.

  An atomic operation refers to an operation that will not be interrupted during execution, either completely executed or not executed at all, and no intermediate state will occur. The volatile keyword only guarantees visibility, that is, when a thread modifies the value of a volatile variable, other threads can immediately see the latest value of the variable instead of using the cached old value.

  However, if multi-step operations are involved, volatile does not guarantee the atomicity of these operations. In a multi-threaded environment, race conditions and inconsistent results between threads may occur.

  Below, we use a simple example to demonstrate that volatile cannot turn non-atomic operations into atomic operations:

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());
    }
}

  In the above example, we created 1000 threads and asked each thread to increment counter 1000 times. Since counter++ is a non-atomic operation, even if counter is declared volatile, the final result may not be 1000 * 1000 = 1000000 as we expected. Because multiple threads may read the same counter value at the same time, then increment and write it back, causing some increment operations to be overwritten.

  To ensure that the increment operation of counter by multiple threads is atomic, you can use the atomic class provided by Java, such as 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();
    }
}

  Using AtomicInteger can ensure the atomicity of the increment operation, so as to get the correct result.

  To sum up, the volatile keyword cannot turn a non-atomic operation into an atomic operation. It can only guarantee the visibility of variables, but it cannot solve the problem of race condition in multi-threaded environment. To ensure atomic operations, you can use the atomic classes provided by Java or use the synchronized keyword to achieve synchronization.

Guess you like

Origin blog.csdn.net/Blue92120/article/details/131933750