定州网站建设最新的网络营销方式
目录
一、处理器如何实现原子操作?
1.使用总线锁保证原子性
1.使用缓存锁保证原子性
二、Java如何实现原子操作?
1)使用循环CAS实现原子操作
2)CAS实现原子操作的三大问题
3)使用锁机制实现原子操作
前言
原子(atomic)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic operation)意 为“不可被中断的一个或一系列操作”。在多处理器上实现原子操作就变得有点复杂。让我们 一起来聊一聊在Intel处理器和Java里是如何实现原子操作的。
一、处理器如何实现原子操作?
1.使用总线锁保证原子性
1.使用缓存锁保证原子性
- 第一种情况是:当操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行 (cache line)时,则处理器会调用总线锁定。
- 第二种情况是:有些处理器不支持缓存锁定。对于Intel 486和Pentium处理器,就算锁定的 内存区域在处理器的缓存行中也会调用总线锁定。
二、Java如何实现原子操作?
1)使用循环CAS实现原子操作
① 什么是CAS
典型的 比较并交换(Compare-And-Swap, CAS)操作,通常用于实现无锁并发编程。CAS 是一种原子操作,用于在多线程环境下确保数据的一致性。
public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
-
方法签名:
public final boolean compareAndSet(int expect, int update)
:-
expect
:期望的当前值。 -
update
:要更新的新值。 -
返回值:如果操作成功返回
true
,否则返回false
。
-
-
方法实现:
unsafe.compareAndSwapInt(this, valueOffset, expect, update)
:-
unsafe
: 是一个Unsafe
类的实例,提供了低级别、不安全的底层由C实现的方法,通常用于直接操作内存。 -
this
: 当前对象的引用。 -
valueOffset
: 当前对象中某个字段的内存偏移量,用于定位该字段在内存中的位置。
-
CAS 工作原理
CAS 操作包含三个操作数:内存位置(由 this
和 valueOffset
确定)、期望值(expect
)。新值(update
)。
CAS 操作的执行过程如下:
-
检查内存位置的当前值是否等于期望值(
expect
)。 -
如果相等,则将内存位置的值更新为新值(
update
),并返回true
。 -
如果不相等,则不进行任何操作,并返回
false
。
② 什么是自旋CAS:当一个线程尝试用CAS操作更新某个共享变量时,若操作失败,线程不会进入阻塞;而是循环进行CAS操作直到成功为止;
自旋 CAS 的特点
-
无锁:自旋 CAS 不需要使用锁(如
synchronized
或ReentrantLock
),避免了锁带来的开销(如上下文切换、线程阻塞等)。 -
乐观并发控制:它前提假设竞争不会频繁发生,因此线程会持续尝试更新,而不是直接进入阻塞状态。
-
适用于低竞争场景:
在低竞争情况下,自旋 CAS 的性能优于锁。
但在高竞争场景下,自旋 CAS 可能导致大量的 CPU 资源浪费(因为线程会不断重试)。 -
可能引发 ABA 问题
2)CAS实现原子操作的三大问题
问题一:ABA问题
CAS在操作值的时候,会检查值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS检查时会发现它的值没有发生变化,但是实际上却变化了。危害如下:
-
数据不一致:如果程序逻辑依赖于变量的变化历史,ABA 问题可能导致数据不一致。
-
逻辑错误:ABA 问题可能导致程序逻辑错误。例如,在无锁链表中,节点的指针可能被错误地更新。
解决思路:就是使用版本号,JDK的Atomic包提供了AtomicStampedReference类来解决ABA问题。该类的compareAndSet方法的作用是首先检查当前引用是否等于预期引用,并且检查当前标志是否等于预期标志,如果全部相等,才进行更新值。
问题二: 长时间CAS不成功开销大
自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销
问题三:只能保证一个共享变量的原子操作
-
对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁;
-
或者将多个变量合并,JDK提供了AtomicReference类来保证引用对象之间的原子性,就可以把多个变量放在一个对象里来进行CAS操作;
3)使用锁机制实现原子操作
锁机制保证了只有获得锁的线程才能够操作锁定的内存区域。JVM内部实现了很多种锁机制:有偏向锁、轻量级锁和互斥锁。有意思的是除了偏向锁,JVM实现锁的方式都用了循环CAS,即当一个线程想进入同步块的时候使用循环CAS的方式来获取锁,当它退出同步块的时候使用循环CAS释放锁。