网站首页 > java教程 正文
CAS概述
CAS(Compare And Swap,比较并交换)通常是之指一种原子操作,针对一个变量首先比较它的内存之与某个期望值是否相等,如果相等就给它赋值新值。CAS操作原子性是直接在硬件层面上得到保证的。CAS可以看作是一种乐观锁(对比数据库的乐观锁、悲观锁)的实现。Java原子类的递增操作就是通过CAS的自旋操作实现的。CAS是一种无锁的算法,在不使用锁(没有线程阻塞)的情况下实现多线程之间的变量同步。
CAS应用
在Java中CAS操作由Unsafe类提供支持,该类提供了三种不同类型变量的操作的native方法,由Java虚拟机提供具体实现,不同虚拟机的实现方式可能会略有差别。
定义一个UnsafeFactory工厂类,通过反射的方式获取Unsafe对象
import java.lang.reflect.Field;
/**
* Unsafe工厂类
* @Author warrior
**/
public class UnsafeFactory {
/**
* 获取 Unsafe 对象
*
* @return
*/
public static Unsafe getUnsafe() {
try {
Field filed = Unsafe.class.getDeclaredField("theUnsafe");
filed.setAccessible(true);
return (Unsafe) filed.get(null);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 获取字段内存偏移量
*
* @param unsafe
* @param clazz
* @param fieldName
* @return
*/
public static long getFieldOffset(Unsafe unsafe, Class clazz, String fieldName) {
try {
long fieldOffset = unsafe.objectFieldOffset(clazz.getDeclaredField(fieldName));
return fieldOffset;
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
}
调用Unsafe#compareAndSwapInt方法进行CAS操作
import sun.misc.Unsafe;
/**
* CAS 应用案例
* @Author warrior
**/
public class CASDemo {
public static void main(String[] args) {
Entity entity = new Entity();
// 获取 Unsafe 对象
Unsafe unsafe = UnsafeFactory.getUnsafe();
// 获取字段内存偏移量
long fieldOffset = UnsafeFactory.getFieldOffset(unsafe, Entity.class, "x");
boolean successful = false;
/**
* 模拟CAS操作
* 4个参数分别是:对象实例、字段的内存偏移量、字段期望值、字段更新值
*/
successful = unsafe.compareAndSwapInt(entity, fieldOffset, 0, 1);
System.out.println(successful+ "\t" + entity.x);
successful = unsafe.compareAndSwapInt(entity, fieldOffset, 1, 3);
System.out.println(successful+ "\t" + entity.x);
successful = unsafe.compareAndSwapInt(entity, fieldOffset, 1, 6);
System.out.println(successful+ "\t" + entity.x);
}
static class Entity {
int x;
}
}
CAS ABA问题
CAS算法实现的一个重要前提是需要取出内存某个时刻的值,在下一个时刻进行比较和替换,那么在这个时间差下会导致数据的变化。当多个线程对一个原子类进行操作时,某个线程短时间内将原子类值从A更改成B,又马上将其修改成A,此时其它线程未感知还是会修改成功。
import java.util.concurrent.atomic.AtomicInteger;
/**
* CAS ABA 案例
*
* @Author warrior
**/
public class CASABADemo {
//定义一个原子变量
private static AtomicInteger atomicInteger = new AtomicInteger(1);
public static void main(String[] args) {
/**
* 创建一个线程将原子变量从1修改到3
*/
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
int value = atomicInteger.get();
System.out.println(Thread.currentThread().getName() + ",获取到的值:" + value);
try {
//休眠1s
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
boolean success = atomicInteger.compareAndSet(value, 3);
if (success) {
System.out.println(Thread.currentThread().getName() + " 修改成功,将值从" + value + "修改成3");
}else {
System.out.println(Thread.currentThread().getName()+" 修改失败");
}
}
},"thread_1");
/**
* 创建一个线程将原子变量从1修改成2在修改成1
*/
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
int value = atomicInteger.get();
System.out.println(Thread.currentThread().getName() + ",获取到的值:" + value);
boolean success = atomicInteger.compareAndSet(value, 2);
if (success) {
System.out.println(Thread.currentThread().getName() + " 修改成功,将值从" + value + "修改成2");
}else {
System.out.println(Thread.currentThread().getName()+" 修改失败");
}
value = atomicInteger.get();
success = atomicInteger.compareAndSet(value, 1);
if (success) {
System.out.println(Thread.currentThread().getName() + " 修改成功,将值从" + value + "修改成1");
}else {
System.out.println(Thread.currentThread().getName()+" 修改失败");
}
}
},"thread_2");
//开启线程
thread1.start();
thread2.start();
}
}
ABA解决方案
数据库有个锁称为乐观锁,是一种基于数据版本实现数据同步的机制,每次修改一次数据,版本就会进行累加。同样,Java也提供了相应的原子引用类AtomicStampedReference。reference即我们实际存储的变量,stamp是版本,每次修改可以通过+1保证版本唯一性。这样就能保证每次修改后版本也会往上递增。
import java.util.concurrent.atomic.AtomicStampedReference;
import java.util.concurrent.locks.LockSupport;
/**
* AtomicStampedReference解决ABA问题案例
*
* @Author warrior
**/
public class AtomicStampedReferenceDemo {
//定义AtomicStampedReference原子变量 Pair.reference值为1, Pair.stamp为1
private static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(1, 1);
public static void main(String[] args) {
/**
* 创建线程1,将原子变量值从1修改到3
*/
Thread thread1 = new Thread(() -> {
int value = stampedReference.getReference();
int stamp = stampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + ",获取值为:" + value + ",版本为:" + stamp);
//阻塞1s
LockSupport.parkNanos(1000L);
//将值从1修改为3
if (stampedReference.compareAndSet(value, 3, stamp, stamp + 1)) {
System.out.println(Thread.currentThread().getName() + ",修改值成功,从1修改为3,版本为:" + stamp);
} else {
System.out.println(Thread.currentThread().getName() + ",修改值失败,版本为:" + stamp);
}
}, "thread1");
/**
* 创建线程2,将原子变量值从1修改到2再修改到1
*/
Thread thread2 = new Thread(() -> {
int value = stampedReference.getReference();
int stamp = stampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + ",获取值为:" + value + ",版本为:" + stamp);
//将值从1修改为2
if (stampedReference.compareAndSet(value, 2, stamp, stamp + 1)) {
System.out.println(Thread.currentThread().getName() + ",修改值成功,从1修改为2,版本为:" + stamp);
} else {
System.out.println(Thread.currentThread().getName() + ",修改值失败");
}
//将值从2修改为1
value = stampedReference.getReference();
stamp = stampedReference.getStamp();
if (stampedReference.compareAndSet(value, 1, stamp, stamp + 1)) {
System.out.println(Thread.currentThread().getName() + ",修改值成功,从2修改为1,版本为:" + stamp);
} else {
System.out.println(Thread.currentThread().getName() + ",修改值失败");
}
}, "thread2");
//开启线程
thread1.start();
thread2.start();
}
}
AtomicMarkableReference 可以理解为上面AtomicStampedReference的简化版,就是不关心修改过几次,仅仅关系是否修改过。因此变量mark是boolean类型,仅记录值是否修改。
import java.util.concurrent.atomic.AtomicStampedReference;
import java.util.concurrent.locks.LockSupport;
/**
* AtomicStampedReference解决ABA问题案例
*
* @Author warrior
**/
public class AtomicStampedReferenceDemo {
//定义AtomicStampedReference原子变量 Pair.reference值为1, Pair.stamp为1
private static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(1, 1);
public static void main(String[] args) {
/**
* 创建线程1,将原子变量值从1修改到3
*/
Thread thread1 = new Thread(() -> {
int value = stampedReference.getReference();
int stamp = stampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + ",获取值为:" + value + ",版本为:" + stamp);
//阻塞1s
LockSupport.parkNanos(1000L);
//将值从1修改为3
if (stampedReference.compareAndSet(value, 3, stamp, stamp + 1)) {
System.out.println(Thread.currentThread().getName() + ",修改值成功,从1修改为3,版本为:" + stamp);
} else {
System.out.println(Thread.currentThread().getName() + ",修改值失败,版本为:" + stamp);
}
}, "thread1");
/**
* 创建线程2,将原子变量值从1修改到2再修改到1
*/
Thread thread2 = new Thread(() -> {
int value = stampedReference.getReference();
int stamp = stampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + ",获取值为:" + value + ",版本为:" + stamp);
//将值从1修改为2
if (stampedReference.compareAndSet(value, 2, stamp, stamp + 1)) {
System.out.println(Thread.currentThread().getName() + ",修改值成功,从1修改为2,版本为:" + stamp);
} else {
System.out.println(Thread.currentThread().getName() + ",修改值失败");
}
//将值从2修改为1
value = stampedReference.getReference();
stamp = stampedReference.getStamp();
if (stampedReference.compareAndSet(value, 1, stamp, stamp + 1)) {
System.out.println(Thread.currentThread().getName() + ",修改值成功,从2修改为1,版本为:" + stamp);
} else {
System.out.println(Thread.currentThread().getName() + ",修改值失败");
}
}, "thread2");
//开启线程
thread1.start();
thread2.start();
}
}
猜你喜欢
- 2025-07-19 Java AtomicInteger操作详解(java attachment)
- 2025-07-19 无锁同步-JAVA之Volatile、Atomic和CAS
- 2025-07-19 Java 作用域详解:从变量可见性到代码封装
- 2025-07-19 CompletableFuture源码分析(fragment源码分析)
- 2025-07-19 Java并发编程从入门到进阶 多场景实战
- 2025-07-19 一文搞懂 CAS 操作与 ABA 问题:高并发编程的关键知识点
- 2025-07-19 Java 内存模型、JVM 内存结构(java的内存模型)
- 2025-07-19 Java面试必备八股文(java面试必备八股文河北人社)
- 2025-07-19 Java面试都在问的CAS,你还没掌握吗?
- 2025-07-19 java VarHandle介绍(java variant)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)