高薪程序员必修课-volatile如何解决并发场景下可见性和有序性的问题

目录

前言

原理

可见性

有序性

示例

示例1:解决可见性问题

示例2:解决有序性问题

总结


前言

        在Java中,volatile 关键字用于解决并发场景下的可见性和有序性问题。通过理解 volatile 的工作原理和使用示例,可以更好地应用它来解决并发编程中的一些常见问题。

原理

可见性

在Java内存模型(JMM)中,每个线程都有自己的本地内存(缓存),线程对变量的读写操作先在本地内存中进行,然后再同步到主内存。这样,当一个线程修改了某个变量的值,其他线程可能无法立即看到这个变化,从而引发可见性问题。

volatile 关键字可以解决这个问题。当一个变量被声明为 volatile,它具有以下特性:

  • 可见性:对 volatile 变量的读写操作会直接操作主内存,而不是线程的本地内存。这意味着,当一个线程修改了 volatile 变量,其他线程立即可见。
有序性

在没有 volatile 的情况下,Java编译器、运行时和处理器可以对代码进行优化,重新排序指令。这可能会导致指令的执行顺序与代码的书写顺序不一致,从而引发有序性问题。

volatile 关键字通过内存屏障(Memory Barriers)确保了指令的有序性:

  • 写屏障:在写入 volatile 变量之前,会插入一个写屏障,确保在此之前的所有写操作都已写入主内存。
  • 读屏障:在读取 volatile 变量之后,会插入一个读屏障,确保在此之后的所有读操作都能从主内存读取最新的值。

示例

下面是一个示例,通过 volatile 关键字解决可见性和有序性问题:

示例1:解决可见性问题
public class VolatileVisibilityExample {
    private static volatile boolean running = true;

    public static void main(String[] args) {
        Thread worker = new Thread(() -> {
            while (running) {
                // Busy-wait loop
            }
            System.out.println("Worker thread stopped.");
        });

        worker.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        running = false;
        System.out.println("Main thread set running to false.");
    }
}

在这个示例中,running 变量被声明为 volatile,确保当主线程将 running 设置为 false 后,工作线程能够立即看到这个变化并终止循环。

示例2:解决有序性问题
public class VolatileOrderingExample {
    private static volatile int x = 0;
    private static volatile int y = 0;

    public static void main(String[] args) {
        Thread writer = new Thread(() -> {
            x = 1; // A
            y = 2; // B
        });

        Thread reader = new Thread(() -> {
            int r1 = y; // C
            int r2 = x; // D
            System.out.println("r1: " + r1 + ", r2: " + r2);
        });

        writer.start();
        try {
            writer.join(); // 确保writer线程先执行完
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        reader.start();
    }
}

在这个示例中,xy 变量被声明为 volatile,确保写线程中的写操作(A和B)在读线程中的读操作(C和D)之前完成,从而保证有序性。在没有 volatile 的情况下,读线程可能会看到 y 已经被赋值为 2,而 x 仍然是初始值 0,导致输出不一致。

总结

  • 可见性volatile 确保变量的修改对所有线程立即可见。任何一个线程对 volatile 变量的修改都会立即写入主内存,并且其他线程对该变量的读取操作会直接从主内存读取最新的值。
  • 有序性volatile 通过内存屏障确保指令的有序执行。在对 volatile 变量的写操作前插入写屏障,确保写操作不会被重新排序;在对 volatile 变量的读操作后插入读屏障,确保读操作不会被重新排序。

通过使用 volatile 关键字,可以在多线程环境下确保变量的可见性和指令的有序性,从而避免竞态条件和内存可见性问题。然而,volatile 仅适用于某些特定场景,对于更复杂的同步问题,仍需使用其他同步机制,如 synchronizedLock 等。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值