CAS 原子操作
CAS
(Compare and Swap)是一种并发算法,通常用于实现多线程环境下的同步操作,特别是在并发编程中实现无锁算法。CAS
操作涉及三个参数:内存位置(V)、期望值(A)和新值(B)。操作的意义是:仅当V的值等于A时,才将V的值更新为B。整个操作是原子的,不会被其他线程中断。
下面是CAS
的基本原理:
- 读取内存值(V): 线程首先读取共享变量的当前值(V)。
-
比较并交换(Compare and Swap): 线程比较读取的值(V)与预期的值(A)。如果相等,说明在读取值的过程中没有其他线程对该变量进行修改,那么线程将新值(B)写入内存位置;否则,说明有其他线程对该变量进行了修改,
CAS
操作失败,线程需要重新尝试。 - 原子性保证: 整个比较并交换的过程是原子性的,即在整个操作过程中,不会被其他线程中断。
CAS
优点: 避免了使用锁带来的性能开销,因为它不会使线程阻塞,而是采用乐观的方式尝试更新共享变量
CAS
缺点:
ABA
问题: 如果一个变量原来的值是A,线程1将其改为B,然后又改回A,此时线程2通过CAS
检查发现值仍然是A,认为没有被修改,但实际上已经发生了变化。为了解决ABA
问题,可以使用版本号等方式引入更多信息。- 循环时间长开销大: 在
CAS
操作失败时,线程需要不断地重试,直到成功为止。这可能导致一些线程长时间无法完成操作,增加了开销。- 只能保证一个共享变量的原子操作:
CAS
只能对单一的共享变量进行原子操作,无法支持类似于整个事务的复合操作。
CAS
示例
public class CASExample {
public static void main(String[] args) {
// 创建一个AtomicInteger,初始值为0
AtomicInteger atomicInteger = new AtomicInteger(0);
// 执行CAS操作,尝试将值从0更新为1
boolean casResult = atomicInteger.compareAndSet(0, 1);
if (casResult) {
System.out.println("CAS success. new value: " + atomicInteger.get());
} else {
System.out.println("CAS failed.");
}
// 尝试再次执行CAS操作,将值从0更新为2,但由于当前值已经是1,操作会失败
casResult = atomicInteger.compareAndSet(0, 2);
if (casResult) {
System.out.println("CAS successful. new value: " + atomicInteger.get());
} else {
System.out.println("CAS failed.");
}
}
}
Connected to the target VM, address: '127.0.0.1:64335', transport: 'socket'
CAS success. new value: 1
CAS failed.
UnSafe 原子操作
Unsafe
类是 Java 中的一个非常特殊且强大的类,它提供了直接访问内存和执行 CAS
(Compare and Swap)等底层操作的方法。然而,Unsafe
类并不是官方公开的 API
,并且在 Java 9 中进行了限制,不再推荐使用。因此,如果可能,最好避免直接使用 Unsafe
类。
以下是一些 Unsafe
类的主要功能:
-
内存操作:
Unsafe
类提供了一些方法,可以直接操作内存,如allocateMemory
、freeMemory
、putXXX
和getXXX
等方法,其中XXX
表示不同的数据类型。 -
对象操作:
Unsafe
类允许直接操作对象的内部字段,比如获取和设置字段的值,甚至可以直接修改对象的类。这些操作可能绕过了 Java 的访问权限检查。 -
CAS
操作:Unsafe
类提供了CAS
相关的方法,例如compareAndSwapInt
、compareAndSwapLong
、compareAndSwapObject
等,用于实现无锁算法。 -
数组操作:
Unsafe
提供了一系列用于操作数组元素的方法,例如putIntVolatile
、getIntVolatile
等。 -
类加载:
Unsafe
类还提供了一些用于加载类和定义类的方法。
AtomicInteger
AtomicInteger
是一种用于执行原子操作的整型类。它常用于在多线程环境下对计数器进行操作。
int get() // 获取当前 AtomicInteger 对象的当前值。
void set(int newValue) // 设置 AtomicInteger 对象的值为指定的新值。
int getAndIncrement() // 原子性地将当前值加 1,并返回加 1 前的值
int incrementAndGet() // 原子性地将当前值加 1,并返回加 1 后的值。
boolean compareAndSet(int expect, int update) // 如果当前值等于预期值 expect,则将当前值更新为新值 update,返回更新是否成功的结果。
int addAndGet(int delta)// 原子性地将当前值与给定的增量 delta 相加,并返回相加后的值。
void lazySet(int newValue) //使用lazySet设置值后,其他线程可能会在之后的一小段时间内读到的还是旧值,更新为newValue有延迟。
AtomicInteger
使用示例
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerExample {
public static void main(String[] args) {
AtomicInteger counter = new AtomicInteger(0);
// 原子性地增加计数器值
int incrementedValue = counter.incrementAndGet();
System.out.println("Incremented Value: " + incrementedValue);
}
}
AtomicBoolean
AtomicBoolean
提供对布尔类型变量的原子操作,通常用于在多线程环境下实现一些状态标记。
// 获取当前 AtomicBoolean 对象的当前值。
boolean get()
// 设置 AtomicBoolean 对象的值为指定的新值。
void set(boolean newValue)
// 原子性地设置 AtomicBoolean 对象的新值,并返回设置前的旧值。
boolean getAndSet(boolean newValue)
// 如果当前值等于预期值 expect,则将当前值更新为新值 update,返回更新是否成功的结果。
boolean compareAndSet(boolean expect, boolean update)
// 与 compareAndSet 方法类似,但不一定提供强制的内存同步。
boolean weakCompareAndSet(boolean expect, boolean update)
AtomicBoolean
使用示例
import java.util.concurrent.atomic.AtomicBoolean;
public class AtomicBooleanExample {
public static void main(String[] args) {
AtomicBoolean flag = new AtomicBoolean(true);
// 原子性地将标志值设置为false
flag.compareAndSet(true, false);
System.out.println("Flag Value: " + flag.get());
}
}
AtomicReference
AtomicReference
允许原子性地操作引用类型变量。下面是一个示例,演示如何原子性地更新引用值
AtomicReference(V initialValue) // 构造函数,创建一个AtomicReference实例,初始值为initialValue。
V get() // 获取当前引用的值。
void set(V newValue) // 设置当前引用的值为newValue。
boolean compareAndSet(V expect, V update) // 如果当前引用的值等于expect,则将当前引用的值设置为update,返回true;否则,返回false。
V getAndSet(V newValue) // 设置当前引用的值为newValue,并返回先前的值。
boolean weakCompareAndSet(V expect, V update) // 类似于compareAndSet,但是对于某些实现,不保证对 expect 和 update 的原子性检查。
String toString() // 返回当前引用的字符串表示形式。
AtomicReference
使用示例
import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceExample {
public static void main(String[] args) {
AtomicReference<String> reference = new AtomicReference<>("initialValue");
// 如果当前值为"initialValue",则设置为"newValue"
reference.compareAndSet("initialValue", "newValue");
System.out.println("Reference Value: " + reference.get());
}
}
AtomicStampedReference
AtomicStampedReference
在 AtomicReference
的基础上增加了版本号,用于解决ABA
问题。文章来源:https://www.toymoban.com/news/detail-808791.html
// 构造方法
AtomicStampedReference(V initialRef, int initialStamp)
// 返回当前引用
V getReference()
// 返回当前标记
int getStamp()
// 返回当前引用,并将当前标记存储在 stampHolder[0] 中
V get(int[] stampHolder)
// 如果当前引用和标记与 expectedReference、expectedStamp 相等,则使用 newReference 和 newStamp 更新引用和标记。该方法在CAS操作的基础上进行判断,避免了ABA问题。
boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)
// 与 compareAndSet 方法相同,但被声明为 weak,通常在不需要保证线程间同步的场景使用。
boolean weakCompareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)
AtomicStampedReference
使用示例文章来源地址https://www.toymoban.com/news/detail-808791.html
import java.util.concurrent.atomic.AtomicStampedReference;
public class AtomicStampedReferenceExample {
public static void main(String[] args) {
AtomicStampedReference<String> stampedReference = new AtomicStampedReference<>("initialValue", 0);
// 如果当前值为"initialValue"且版本号为0,则设置为"newValue"和版本号1
stampedReference.compareAndSet("initialValue", "newValue", 0, 1);
System.out.println("Stamped Reference Value: " + stampedReference.getReference());
System.out.println("Stamped Reference Stamp: " + stampedReference.getStamp());
}
}
到了这里,关于Java并发 - 原子类的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!