volatile(转)

本文详细解释了volatile关键字在编程中的用途,特别是在防止编译器优化、确保数据一致性和同步方面的作用。通过具体示例介绍了volatile如何帮助处理中断服务程序、多任务环境下的共享标志以及存储器映射的硬件寄存器等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

volatile的用法

避免编译器优化的用法 转自<海涛的笔记>   _lindwen
volatile的本意是“易变的”
由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:
static int i=0;
int main(void)
{
...
while (1)
{
if (i) dosomething();
}
}
/* Interrupt service routine. */
void ISR_2(void)
{
i=1;
}
程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此
可能只执行一次对从i到某寄存器的读***作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被
调用。如果将将变量加上volatile修饰,则编译器保证对此变量的读写***作都不会被优化(肯定执行)。此例中i也应该如此说明。
一般说来,volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;
另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实
现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。
volatile 的含义
volatile总是与优化有关,编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以死代码消除。但有时这些优化不是程序所需要的,这时可以用volatile关键字禁止做这些优化,volatile的字面含义是易变的,它有下面的作用:
1 不会在两个***作之间把volatile变量缓存在寄存器中。在多任务、中断、甚至setjmp环境下,变量可能被其他的程序改变,编译器 自己无法知道,volatile就是告诉编译器这种情况。
2 不做常量合并、常量传播等优化,所以像下面的代码:
volatile int i = 1;
if (i > 0) ...
if的条件不会当作无条件真。
3 对volatile变量的读写不会被优化掉。如果你对一个变量赋值但后面没用到,编译器常常可以省略那个赋值***作,然而对Memory Mapped IO的处理是不能这样优化的。
前面有人说volatile可以保证对内存***作的原子性,这种说法不大准确,其一,x86需要LOCK前缀才能在SMP下保证原子性,其二,RISC根本不能对内存直接运算,要保证原子性得用别的方法,如atomic_inc。
对于jiffies,它已经声明为volatile变量,我认为直接用jiffies++就可以了,没必要用那种复杂的形式,因为那样也不能保证原子性。
你可能不知道在Pentium及后续CPU中,下面两组指令
inc jiffies
;;
mov jiffies, %eax
inc %eax
mov %eax, jiffies
作用相同,但一条指令反而不如三条指令快。

资源下载链接为: https://pan.quark.cn/s/22ca96b7bd39 在当今的软件开发领域,自动化构建与发布是提升开发效率项目质量的关键环节。Jenkins Pipeline作为一种强大的自动化工具,能够有效助力Java项目的快速构建、测试及部署。本文将详细介绍如何利用Jenkins Pipeline实现Java项目的自动化构建与发布。 Jenkins Pipeline简介 Jenkins Pipeline是运行在Jenkins上的一套工作流框架,它将原本分散在单个或多个节点上独立运行的任务串联起来,实现复杂流程的编排与可视化。它是Jenkins 2.X的核心特性之一,推动了Jenkins从持续集成(CI)向持续交付(CD)及DevOps的变。 创建Pipeline项目 要使用Jenkins Pipeline自动化构建发布Java项目,首先需要创建Pipeline项目。具体步骤如下: 登录Jenkins,点击“新建项”,选择“Pipeline”。 输入项目名称描述,点击“确定”。 在Pipeline脚本中定义项目字典、发版脚本预发布脚本。 编写Pipeline脚本 Pipeline脚本是Jenkins Pipeline的核心,用于定义自动化构建发布的流程。以下是一个简单的Pipeline脚本示例: 在上述脚本中,定义了四个阶段:Checkout、Build、Push packageDeploy/Rollback。每个阶段都可以根据实际需求进行配置调整。 通过Jenkins Pipeline自动化构建发布Java项目,可以显著提升开发效率项目质量。借助Pipeline,我们能够轻松实现自动化构建、测试部署,从而提高项目的整体质量可靠性。
在 Java 多线程编程中,`private volatile` 的使用通常涉及对变量可见性内存一致性的控制。当一个变量被声明为 `volatile`,它会告诉编译器运行时系统不要对该变量进行某些优化(例如缓存或重排序),以确保所有线程都能看到该变量的最新值。 ### `volatile` 关键字的作用 1. **保证变量的可见性**:在多线程环境中,每个线程可能会将共享变量复制到自己的本地缓存中以提高性能。如果一个变量是 `volatile` 类型,则每次读取都会从主内存中获取,而不是线程的本地缓存;同样,写操作也会立即刷新到主内存中,从而确保其他线程可以及时看到更新后的值[^2]。 2. **禁止指令重排序**:JVM CPU 为了优化性能可能会对指令进行重排序,但这种行为在并发环境下可能导致不可预测的结果。`volatile` 变量的读写操作不会被重排序,并且它们之间有“happens-before”关系,这有助于维护程序执行的一致性[^3]。 3. **提供轻量级同步机制**:虽然 `synchronized` 块提供了更强的互斥访问能力,但是 `volatile` 提供了一种更轻便的方式来实现简单的状态标志变更等场景下的线程间通信[^1]。 ### 使用示例 考虑一个简单的例子,其中有一个私有的 `volatile boolean` 标志用于通知工作线程停止运行: ```java public class Worker { private volatile boolean running = true; public void start() { new Thread(() -> { while (running) { // 执行任务... } }).start(); } public void stop() { running = false; } } ``` 在这个例子中,`running` 被声明为 `private volatile`,这样当调用 `stop()` 方法修改 `running` 的值时,正在循环中的线程能够立刻感知到这个变化并退出循环[^2]。 ### 注意事项与限制 - `volatile` 不保证原子性:对于像递增这样的复合操作 (`i++`) 来说,即使 `i` 是 `volatile` 类型也不能保证整个操作是原子的。这类情况下仍然需要使用 `synchronized` 或者 `AtomicInteger` 等类来确保原子性。 - `volatile` 适用于状态标记、一次性安全发布以及独立观察者模式等情况,但在处理复杂的状态换或多变量依赖时应谨慎使用[^2]。 ### 相关问题 1. 在 Java 中,除了 `volatile` 之外还有哪些方法可以用来确保线程间的变量可见性? 2. `volatile` `synchronized` 之间的主要区别是什么?各自适合什么样的应用场景? 3. 如果多个线程同时对一个非原子性的 `volatile` 变量进行读写操作,会发生什么情况?如何避免这些问题? 通过上述讨论可以看出,`private volatile` 在 Java 多线程编程中扮演着重要角色,尤其是在需要简单地共享状态而不采用重量级锁的情况下。然而,开发者也必须清楚其局限性正确的使用方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值