Java 中 volatile 关键字


共计 1930 个字符,预计需要花费 5 分钟才能阅读完成。

并发编程中,有三个非常重要的概念:

  • 原子性:提供了一种互斥访问,同一时刻只能有一个线程对它进行操作

  • 可见性:一个线程对主内存对修改可以及时的被其他线程观察到

  • 有序性:一个线程观察其他线程的指令行执行顺序,由于指令重新排序的存在,该观察结果一般杂乱无序

volatile 关键字能够保证以下两点:

  1. 可见性(Visibility):

当一个线程修改了一个volatile变量的值时,这个新值会立即被写入到主内存中,而不会先缓存在线程的本地内存中。同时,当其他线程读取这个volatile变量时,它们会直接从主内存中读取最新的值,而不会使用本地缓存中的旧值。这样就确保了多个线程之间对共享变量的操作是可见的。

  1. 禁止指令重排序(Prevents Instruction Reordering):

volatile关键字会禁止指令重排序优化,即在代码中volatile变量之前的操作不会被重排序到volatile变量之后,也不会被重排序到volatile变量之后的操作之前。这样可以确保指令的顺序按照程序的顺序执行,避免了可能会影响多线程程序正确性的指令重排序。

需要注意的是,虽然volatile关键字能够保证可见性和禁止指令重排序,但它并不能保证原子性,即不保证多线程操作的原子性。

另外,volatile 和 CAS(Compare and Swap,比较并交换)通常结合在一起使用,主要用于多线程编程中实现原子操作,特别是在并发环境下确保数据的一致性和可见性。

关于 CAS,java.util.concurrent.atomic包提供了一系列原子操作的类,其中就包括了基于 CAS 的实现。最常见的就是java.util.concurrent.atomic.AtomicIntegerjava.util.concurrent.atomic.AtomicLong等,它们提供了诸如compareAndSet()等方法,用于在不使用锁的情况下进行原子性的操作。CAS 的使用可以提高并发性能,因为它避免了使用锁带来的线程阻塞和上下文切换。

使用 volatile 和 CAS 来实现一个线程安全的计数器:

import java.util.concurrent.atomic.AtomicInteger;

public class VolatileCASExample {
    private volatile int counter = 0;

    public int getCounter() {
        return counter;
    }

    public void increment() {
        int currentValue;
        int newValue;
        do {
            currentValue = counter; // 获取当前值
            newValue = currentValue + 1; // 计算新值
        } while (!compareAndSwap(currentValue, newValue)); // CAS 操作,如果失败则重试
    }

    private synchronized boolean compareAndSwap(int expect, int update) {
        if (counter == expect) { // 检查当前值是否与期望值相等
            counter = update; // 如果相等,则更新为新值
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        final VolatileCASExample example = new VolatileCASExample();
        final AtomicInteger total = new AtomicInteger(0);

        // 创建多个线程并发增加计数器值
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
                total.incrementAndGet();
            }
        };

        // 启动多个线程
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(task);
            threads[i].start();
        }

        // 等待所有线程完成
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 输出计数器的最终值
        System.out.println("Final counter value: " + example.getCounter());
        System.out.println("Total increments: " + total.get());
    }
}

提醒:本文发布于109天前,文中所关联的信息可能已发生改变,请知悉!

Tips:清朝云网络工作室

阅读剩余
THE END