并发时用到的关键字 volatile

Java中volatile关键字在并发场景的应用
博客主要探讨Java中volatile关键字,以单例模式中对instance修饰为例。通过并发场景下两条子线程对成员变量赋值的代码运行结果,解释了CPU特性导致的问题,如非原子性、线程不安全等。还说明了用volatile修饰变量后,程序打印情况的变化。

volatile 这个关键字,接触最多的场景是单利,单利中对 instance 做修饰,这个词怎么解释?知道看了一个例子,才明白些。并发中,如果两个线程不停的对一个成员变量赋值,会产生什么效果呢?

    static boolean flag;
    static class ThreadA extends Thread {
        @Override
        public void run() {
            while (true) {
                if (flag) {
                    flag = false;
                    System.out.println("The flag is set false");
                }
            }
        }
    }

    static class ThreadB extends Thread {
        @Override
        public void run() {
            while (true) {
                if (!flag) {
                    flag = true;
                    System.out.println("The flag is set true");
                }
            }
        }
    }

    public static void main(String[] args) {
        new ThreadA().start();
        new ThreadB().start();
    }

按照我的预想,运行上面的代码,两条子线程应该是依次打印日志,但结果却是

The flag is set true
The flag is set true
The flag is set false
The flag is set false
The flag is set false
The flag is set true
The flag is set true
The flag is set true
The flag is set false
The flag is set false
The flag is set false
The flag is set true
The flag is set true

与预想的不一样,并且居然打印了一点日志后不打印了; 两条子线程都是活着,没有被杀死,但为什么没继续打印日志?原来CPU有一些自己的特点。我们会提到原子性等,比如 int i = 2; i++; 我们会说它不是原子性的,不是线程安全,这里也差不多,由于CPU有间隔执行程序,所以就导致并发中,flag 赋值就被覆盖了。这种情况下, flag 用 volatile 修饰后,再次打印,

The flag is set false
The flag is set false
The flag is set false
The flag is set true
The flag is set true
The flag is set true
The flag is set false
The flag is set false
The flag is set false
The flag is set true
The flag is set true
The flag is set true
The flag is set false
The flag is set false
The flag is set false
The flag is set true
The flag is set true

会一直打印,但也不是依次打印,这点要注意。
 

<think>好的,我现在需要介绍一下volatile关键字。首先,我得回忆一下volatile的基本概念。记得volatile在编程中用于告诉编译器某个变量可能会在程序之外被改变,所以编译器不要对这个变量进行优化。对吧?比如,在嵌入式系统中,硬件寄存器的状态可能会被外部设备修改,这候就需要用volatile来确保每次访问都从内存中读取,而不是使用寄存器中的缓存值。 不过,我可能记混了volatile的具体应用场景。有没有其他情况也需要用到volatile呢?比如多线程环境中,某个变量可能被多个线程访问,这候是否应该用volatile?好像不是,因为多线程的数据同步问题通常需要用互斥锁或者原子操作,而不是仅仅依赖volatilevolatile并不能保证原子性,只是防止编译器优化,确保每次读取都从内存中获取。这一点需要明确,否则可能会误导别人。 接下来,我得想想volatile和const一起使用的情况。如果一个变量既是volatile又是const,那表示什么呢?可能表示这个变量在程序本身不会被修改,但可能被外部因素改变,比如只读的硬件状态寄存器。这种情况下,用const volatile可以同表达这两种属性。 然后,volatile在C/C++中的语法是怎样的?声明变量的候加上volatile关键字,比如volatile int x;。这样编译器在处理x的候,就不会做某些优化,比如把多次读取x的操作合并成一次,或者忽略掉看似无用的读取操作。例如,循环中检测某个状态变量,如果没有volatile,编译器可能会优化掉这个检测,导致死循环。 不过,有些细节可能需要进一步确认。比如,volatile是否会影响内存的可见性?在Java中,volatile关键字不仅防止指令重排序,还保证变量的修改对所有线程立即可见。但C/C++中的volatile并不提供线程间的可见性保证,这可能是一个重要的区别点,需要特别指出。 另外,有没有一些常见的错误用法?比如,误以为volatile可以替代互斥锁,结果导致竞态条件。这候应该强调volatile的局限性,尤其是在多线程编程中的不足,避免用户滥用。 还有,不同编译器对volatile的处理可能不同,这会不会导致可移植性问题?可能需要提到这一点,但具体情况可能比较复杂,作为介绍性的内容,可能不需要深入展开,但可以提醒用户注意编译器差异。 最后,总结volatile的主要用途:防止编译器优化,确保每次访问都直接读写内存,适用于硬件寄存器和被信号处理程序修改的变量等情况。同,指出其不适用于多线程同步的问题,应该使用适当的同步机制。 在组织语言的候,要结构清晰,分点说明,确保用户能逐步理解。可能需要先定义volatile,然后讲作用,应用场景,注意事项,常见误区,最后总结。同,注意使用正确的术语,避免技术错误。</think>以下是对`volatile`关键字的详细介绍: --- ### **1. 基本定义** `volatile`是C/C++中的一个关键字,用于修饰变量,表明该变量的值可能以**程序不可预知的方式被修改**(例如硬件寄存器、中断服务程序、多线程共享变量等)。 编译器在优化代码,通常会假设变量值仅由程序逻辑控制。使用`volatile`会强制编译器**每次访问变量都直接从内存读写**,而非使用寄存器中的缓存值。 --- ### **2. 主要作用** - **禁用编译器优化** 例如,以下代码中,若变量`flag`未被声明为`volatile`,编译器可能将`while(flag)`优化为`while(true)`,导致死循环: ```c volatile int flag = 0; while(flag) { /* 等待外部修改flag */ } ``` - **确保内存可见性** 每次对`volatile`变量的操作都会直接读写内存,而非依赖临缓存(但对多线程场景的可见性保障不足,需配合其他机制)。 --- ### **3. 典型应用场景** - **硬件寄存器访问** 嵌入式开发中,硬件寄存器的值可能被外部设备改变,需用`volatile`声明: ```c volatile uint32_t* const reg = (uint32_t*)0x1234; ``` - **信号处理程序** 若变量在信号处理函数中被修改,需声明为`volatile`以防止编译器优化: ```c volatile sig_atomic_t signal_received = 0; ``` - **多线程共享变量(有限场景)** `volatile`可避免编译器错误优化,但无法替代锁或原子操作(不保证原子性)。 --- ### **4. 常见误区** - **误认为`volatile`适用于多线程同步** 错误示例: ```c volatile int counter = 0; // 线程A: counter++; // 线程B: counter--; ``` **问题**:`volatile`不保证操作的原子性,`counter++`实际包含“读-改-写”三步,可能引发竞态条件。 - **与`const`结合使用** `const volatile`表示变量在程序中不可修改,但可能被外部修改: ```c const volatile int sensor_value = 0; // 程序不修改,但传感器可能更新 ``` --- ### **5. 与Java/C#中volatile的区别** - **C/C++**:仅禁用编译器优化,不保证多线程可见性或原子性。 - **Java/C#**:额外保证内存可见性和禁止指令重排序(通过内存屏障实现)。 --- ### **6. 总结** | 场景 | 是否使用`volatile` | |--------------------|---------------------------| | 硬件寄存器/中断变量 | ✔️ | | 信号处理程序变量 | ✔️ | | 多线程共享变量 | ❌(需锁或原子操作) | **关键点**:`volatile`解决的是编译器的过度优化问题,而非并发控制问题。在多线程编程中,应优先使用互斥锁、原子变量或内存屏障等机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值