news 2026/9/11 10:51:42

Java多线程:CAS从原理到应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java多线程:CAS从原理到应用

CAS是Java多线程开发中一个基础概念,包括很多其他的中间件都会用到它。那么我们就从原理到应用,一起去揭开它的面纱。

一、CAS原理

CAS,Compare and Swap,比较并交换。是通过原子指令的方式实现在多线程下的同步功能。

在实现过程中,通过把前后两次获取到的变量在内存中的值进行比对,如果相等则对变量值进行更改,否则重新读取内存的值,然后重复上述过程。

在Java中,CAS是属于乐观锁的一种,也就是那种不进行加锁,而是假定在没有冲突的条件下去完成一个操作,如果中途发生冲突(例如变量的内存值被改动过,导致V和A值不相等等情况)导致失败后马上进行重试,直到成功。

既然有乐观锁,那么与之对应的就是悲观锁。synchronized 就是悲观锁的一种,这种会对作用范围内的资源进行锁定,其他线程需要等待所释放才能获取这把锁。

二、CAS的不足

虽然说CAS的使用广泛,但是其会出现三个主要的问题:

1、ABA问题

在做CAS更新操作中,读取到变量的值A,在准备为变量赋值的时候期望值依旧为A,但是变量值可能已被多次修改后充值改回A,这个CAS的更新漏洞就被叫做ABA。

2、循环时间长开销大

自旋CAS的方式如果长时间不成功,会给CPU带来很大的开销。

3、只能保证一个共享变量的原子操作

只对一个共享变量操作可以保证原子性,但是多个则不行。

三、CAS在Java中的应用

Java中大量使用CAS,比较集中在java.util.concurrent包内。

为了解决ABA问题,Java提供多个解决方案

1、AtomicStampedReference

package multithread.cas; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicStampedReference; public class AtomicReferenceTest { public static void main(String[] args) { boolean result = CasUtil.increment(); System.out.println("result:"+result); result = CasUtil.increment(); System.out.println("result:"+result); } public static class CasUtil { public static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference(1,1); //自增操作 public static boolean increment() { boolean result = atomicStampedReference .compareAndSet(atomicStampedReference.getReference(), //expectedReference,引用的期望值 atomicStampedReference.getReference()+1, atomicStampedReference.getStamp(),//expectedStamp,版本戳的期望值 atomicStampedReference.getStamp()+1); while(!result) { System.out.println("result is false"); return increment(); } return true; } } }

设置atomicStampedReference.getReference()和atomicStampedReference.getStamp()这两条语句保证了期望值和原值必然一致,所以main方法打印结果一直是“true”。

接下来我们分析代码,进入到AtomicStampedReference类的compareAndSet方法,其获取内存值与传入的期待值比较并返回比较结果。在前三个比较项如有一项不成立(一般是第三项),进行第四个条件操作即调用casPair方法

casPair方法,调用UNSAFE.compareAndSwapObject。

private static final long pairOffset = objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class); /** * Atomically sets the value of both the reference and stamp * to the given update values if the * current reference is {@code ==} to the expected reference * and the current stamp is equal to the expected stamp. * * @param expectedReference the expected value of the reference * @param newReference the new value for the reference * @param expectedStamp the expected value of the stamp * @param newStamp the new value for the stamp * @return {@code true} if successful */ public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) { Pair<V> current = pair; return expectedReference == current.reference && //判断旧引用,和当前的是否一样;判断 expectedStamp == current.stamp && //旧版本,和当前的是否一样 ((newReference == current.reference &&newStamp == current.stamp) || //判断引用和版本是否都相等,也就是替换和被替换的是同一个对象就不需要再调用casPair了 casPair(current, Pair.of(newReference, newStamp))); } private boolean casPair(Pair<V> cmp, Pair<V> val) { return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);//调用UNSAFE的compareAndSwapObject方法进行对象比较及修改 } private static class Pair<T> { final T reference; final int stamp; private Pair(T reference, int stamp) { this.reference = reference; this.stamp = stamp; } static <T> Pair<T> of(T reference, int stamp) { return new Pair<T>(reference, stamp); //每次返回一个新Pair对象 } } static long objectFieldOffset(sun.misc.Unsafe UNSAFE, String field, Class<?> klazz) { try { return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field)); } catch (NoSuchFieldException e) { // Convert Exception to corresponding Error NoSuchFieldError error = new NoSuchFieldError(field); error.initCause(e); throw error; } }

UNSAFE类的代码

public native long objectFieldOffset(Field var1); public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 10:49:58

CANfestival移植实战:STM32F1轻松实现CANopen从站节点

简介&#xff1a;基于CANfestival的CANopen协议在STM32F1系列单片机上的实现&#xff0c;是一份面向嵌入式开发者的完整工程资料&#xff0c;适合自动化、电子信息、通信工程等专业用于毕业设计、课程设计与项目初期开发。包内不仅包含协议栈移植的完整源码、详细配置文档&…

作者头像 李华
网站建设 2026/9/11 10:49:56

光波导技术解析:原理、设计与应用实践

1. 光波导基础概念解析 光波导是现代光子学中最基础也最重要的元件之一&#xff0c;它本质上是一种能够约束和引导光波传输的结构。就像水管引导水流一样&#xff0c;光波导通过特定的介质结构将光能限制在特定路径中传播。这种结构通常由高折射率的核心层和低折射率的包层组成…

作者头像 李华
网站建设 2026/9/11 10:49:52

Doris与StarRocks:新一代OLAP引擎核心技术对比

1. 新一代OLAP引擎之争&#xff1a;Doris与StarRocks深度对比在数据分析领域&#xff0c;OLAP&#xff08;在线分析处理&#xff09;引擎一直是企业数据仓库的核心组件。随着数据量的爆炸式增长和实时分析需求的提升&#xff0c;传统OLAP方案逐渐暴露出性能瓶颈。Apache Doris和…

作者头像 李华
网站建设 2026/9/11 10:46:23

基于STM32的鱼缸水质监测系统DIY设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 10:44:50

SL3161A降压芯片详解:100V输入/固定5V/2A车载工业电源方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 10:42:45

电压型VSG离网仿真建模与MATLAB实现指南

1. 电压型虚拟同步发电机&#xff08;VSG&#xff09;离网仿真模型搭建指南作为一名电力电子工程师&#xff0c;我最近在微电网项目中遇到了一个棘手的问题&#xff1a;如何让逆变器在离网状态下模拟同步发电机的运行特性&#xff1f;经过反复尝试&#xff0c;最终通过搭建电压…

作者头像 李华