首页常见问题正文

volatile能使得一个非原子操作变成原子操作吗?

更新时间:2023-07-26 来源:黑马程序员 浏览量:

IT培训班

  在Java中,volatile关键字可以用于修饰变量,用于保证可见性和防止指令重排序。但是,volatile不能将一个非原子操作变成原子操作。

  原子操作是指在执行过程中不会被中断的操作,要么完全执行,要么完全不执行,不会出现中间状态。volatile关键字只保证了可见性,即当一个线程修改了volatile变量的值后,其他线程能够立即看到该变量的最新值,而不是使用缓存的旧值。

  然而,如果涉及到多步骤的操作,volatile并不能保证这些操作的原子性。在多线程环境下,可能会出现线程间的竞态条件和不一致的结果。

  下面,我们通过一个简单的示例来演示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());
    }
}

  在上面的例子中,我们创建了1000个线程,并让每个线程执行1000次对counter的增加操作。由于counter++是一个非原子操作,即使counter被声明为volatile,最终得到的结果也可能不是我们期望的1000 * 1000 = 1000000。因为多个线程可能同时读取相同的counter值,然后进行递增并写回,导致部分递增操作被覆盖。

  要保证多个线程对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可以确保递增操作的原子性,从而得到正确的结果。

  总结起来,volatile关键字不能将非原子操作变成原子操作。它只能保证变量的可见性,但无法解决多线程环境下的竞态条件问题。要保证原子操作,可以使用Java提供的原子类或者使用synchronized关键字来实现同步。

分享到:
在线咨询 我要报名
和我们在线交谈!