Volatile是什么,CAS是什么:

本文介绍了Volatile,它是轻量级同步机制,能保证可见性、禁止指令重排,但不保证原子性。还阐述了内存屏障的作用。对于原子性问题,可使用原子操作类如AtomicInteger,其底层原理是CAS。同时分析了CAS的缺点及解决ABA问题的方法。

一句话说明白:啥是Volatile,其实就是轻量级的同步机制:保证可见性,不保证原子性,禁止指令重排。

 记住:Volatile就是乞丐版的synchronizid	。

我们主要从三部分理解volatile:保证可见性,不保证原子性,禁止指令重排。

  • 保证可见性和禁止指令重排的理解:

volatile实现禁止指令重排优化,从而避免多线程环境下程序出现乱序执行的现象。
先了解一个概念,内存屏障又称内存栅栏,是一个CPU指令,它的作用有两个:
一是保证特定操作的执行顺序
二是保证某些变量的内存可见性(利用该特性实现volatile的内存可见性)

由于编译器和处理器都能执行指令重排优化。如果在指令间插入一条Memory Barrier则告诉编译器和CPU,不管什么指令都不能和这条Memory Barrier指令重新排序,也就是说
通过插入内存屏障禁止在内存屏障前后的指令执行重排序优化。内存屏障另外一个作用是强制刷出各种CPU的缓存数据,因此任何CPU上的线程都能读取到这些数据的最新版本。

  • 不保证原子性的理解:(了解一下JMM)

在这里插入图片描述
主内存的介绍:硬件上指的是我们日常用的比如8G 内存条。
在这里插入图片描述
在这里插入图片描述
解决原子性:用原子操作的类:比如AtomicInteger。

CAS是什么:
解决原子性:用原子操作的类:比如AtomicInteger。那么AtomicInteger的底层原理是什么,底层原理其实就是cas.
CAS 就是比较并交换。
AtomicInteger.compareAndSet()-----底层是compareAndSwap.
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
怎么理解:根据JMM*(Java内存模型)来看,设主物理内存中的变量temp的内存值是V,线程一的预期值为A,其实就是私有线程中的主内存的线程副本temp的缓存值为A,线程一想要把temp的值V修改为新值B,此时对比V是否等于A,如果相等,说明没有其他线程对主内存中的值进行修改,那么可以进行修改,否则就说明其他线程对主内存中的值进行了修改,那么线程一不能进行直接修改,想要修改,必须重新把V的值读入自己的缓存中,然后再重新进行下一轮比较。以此类推

底层原理:
atomicInter.getAndIncrement()底层是unsafe.getAndAddInt(this,valueoffset,i)
1.Unsafe
是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据。
Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为Java中CAS操作的执行依赖于Unsafe类的方法。

注意Unsafe类中的所有方法都是native修饰的,也就是说Unsafe类中的方法都直接调用操作系统底层资源执行相应任务。

2.变量valueOffset,表示该变量在内存中的
偏移地址,因为Unsafe就是根据内存偏移地址获取数据的。

3.变量value用volatile修饰,保证了多线程之间的内存可见性。
总结来看,CAS靠的是汇编指令中的CPU原语操作来保证原子性。

CAS的缺点:
1、 循环时间长开销大:如果CAS失败,会一直进行尝试。如果CAS长时间一直不成功,可能会给CPU带来很大的开销。不断自旋
2、 只能保证一个共享变量的原子操作:当对一个共享变量执行操作时,我们只能使用循环CAS的方式来保证原子操作,但是,对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁来保证原子性。
3、 可能会引发ABA问题:
ABA问题其实就是狸猫换太子;产生原因:CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并替换,那么在这个时间差类会导致数据的变化。

比如说:主存的内存值为5,现在有两个线程:T1,T2。初始情况,T1,T2都从主内存中拷贝5,假设T1,两秒钟对自身5修改一次,T2十秒钟对自身5修改。现在T1线对自身5修改为2,之后写入主内存,此时主内存为2,T1为2,T2中的值为5,又过了两秒T1又把2改为5,重新写入主内存,到了第十秒,T2来读主内存比较,发现还是5,就以为主内存的值没有被其他线程动过。其实这种以为是错误的。

解决ABA问题用原子时间戳引用,用AtomicStampledReference,就是每次改变都加一个时间戳,CAS过程中不仅要比较内存值和预测值还要比较时间戳。

`volatile` 不是注解(Annotation),而是一个 **Java 关键字**,用于修饰变量(字段),它在多线程编程中起到非常关键的作用。 --- ### ✅ `volatile` 关键字的作用: `volatile` 的主要作用是: > **保证变量在多个线程之间的可见性**。 也就是说,当一个线程修改了 `volatile` 修饰的变量,其他线程可以**立即看到这个修改**,避免因为线程缓存导致的数据不一致问题。 --- ### ✅ 使用场景: 适用于以下情况: - 一个变量被多个线程访问。 - 不涉及复合操作(如自增 `i++`)。 - 只需要保证可见性,不需要保证原子性。 --- ### ✅ 示例代码: ```java public class MyTask implements Runnable { private volatile boolean isRunning = true; @Override public void run() { while (isRunning) { // 执行任务... } } public void stop() { isRunning = false; // 修改后其他线程能立即看到 } } ``` 在这个例子中: - `isRunning` 被 `volatile` 修饰。 - 当主线程调用 `stop()` 方法将其设为 `false` 时,运行 `run()` 的线程会立即看到这个变化并退出循环。 --- ### ✅ `volatile` 和 `synchronized` 的区别: | 特性 | `volatile` | `synchronized` | |------|------------|----------------| | 类型 | 变量修饰符 | 方法或代码块修饰符 | | 作用 | 保证可见性和有序性 | 保证原子性、可见性和有序性 | | 是否阻塞 | ❌ 不阻塞 | ✅ 阻塞等待 | | 性能 | ✅ 性能更高 | ❌ 性能较低 | | 是否能保证原子性 | ❌ 不能 | ✅ 能 | --- ### ✅ `volatile` 和 `AtomicInteger` 的区别: | 特性 | `volatile` | `AtomicInteger` | |------|------------|----------------| | 是否保证原子性 | ❌ | ✅ | | 适用场景 | 只读或简单赋值 | 需要原子操作(如自增) | | 底层实现 | 内存屏障 | CAS(Compare and Swap) | --- ### ✅ 注意事项: 1. `volatile` **不能替代锁**(如 `synchronized`),因为它不保证原子性。 2. 如果变量涉及多个操作(如读-改-写),应使用 `AtomicInteger` 或加锁。 3. `volatile` 可以禁止指令重排序,保证有序性。 --- ### ✅ 总结一句话: > `volatile` 是 Java 中用于保证变量在多线程间可见的关键字,但它**不保证原子性**,不能替代锁。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值