千家信息网

java多线程CAS的介绍

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,这篇文章主要讲解了"java多线程CAS的介绍",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"java多线程CAS的介绍"吧!1 CAS讲解在工作中,我
千家信息网最后更新 2026年11月02日java多线程CAS的介绍

这篇文章主要讲解了"java多线程CAS的介绍",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"java多线程CAS的介绍"吧!

1 CAS讲解

在工作中,我们往往需要面对多线程计数的问题,我们第一反应,使用"synchronized",控制并发。

@Slf4jpublic class CASDemo extends Thread{    private static  int  t = 0;    @Override    public void run() {        increment();        log.info("-----------{}------------",t);    }    private synchronized  static  void increment() {        t++;    }    public static void main(String[] args){        for (int i = 0 ; i < 100 ; i++){            CASDemo casDemo = new CASDemo();            casDemo.start();        }    }

结果如下:

从上面结果来看符合我们的预期,100个线程执行了1次,最后t为100。但是这是最好的实现机制吗?我们知道"synchronized"是同步锁,即使jdk6以后已经对其进行了优化(具体可以见另一篇文章:java多线程基础知识之synchronized原理分析),只是用来计数,是否太大材小用了。有没有一种更加优雅的解决方案?

@Slf4jpublic class CASDemo extends Thread{    private static AtomicInteger t = new AtomicInteger(0);    @Override    public void run() {        log.info("-----------{}------------",t.incrementAndGet());    }    public static void main(String[] args){        for (int i = 0 ; i < 100 ; i++){            CASDemo casDemo = new CASDemo();            casDemo.start();        }    }}

结果如下:

从结果上来看,第一种和第二种结果是一样的。为啥推荐第一种方法呢?因为第二种使用了无锁式的"compareAndSwap"即"CAS",既然"CAS"是无锁的,那么是怎么样保证其实线程安全的。

"compareAndSwap"从字面上看,这一过程肯定包含了比较和替换两个动作,具体步骤如下:

(1)线程从内存中读取 i 的值,假如此时 i 的值为 0,我们把这个值称为 k 吧,即此时 k = 0。

(2)令 j = k + 1。

(3)用 k 的值与内存中i的值相比,如果相等,这意味着没有其他线程修改过 i 的值,我们就把 j(此时为1) 的值写入内存;如果不相等(意味着i的值被其他线程修改过),我们就不把j的值写入内存,而是重新跳回步骤 1,继续这三个操作。具体源码如下:

同时,整个"CAS"是原子的,对应操作系统的一条硬件操作指令,尽管看似有很多操作在里面,但操作系统能够保证它是原子执行的。

通过上面的流程讲解,我们可以发现其不可能从内存中同时取到相同的K值,并且分别+1然后提交到内存中,从而保证的线程安全。

但是我们仍然面临一个问题:谁偷偷更改了我的值。

举个例子,当线程A即将要执行第三步的时候,线程 B 把 i 的值加1,之后又马上把 i 的值减 1,然后,线程 A 执行第三步,这个时候线程 A 是认为并没有人修改过 i 的值,因为 i 的值并没有发生改变。而这,就是我们平常说的ABA问题。对于基本类型的值来说,这种把数字改变了在改回原来的值是没有太大影响的,但如果是对于引用类型的话,就会产生很大的影响了。

怎么解决这个问题呢?--版本控制(参考乐观锁)。

例如,每次有线程修改了引用的值,就会进行版本的更新,虽然两个线程持有相同的引用,但他们的版本不同,这样,我们就可以预防 ABA 问题了。Java 中提供了 AtomicStampedReference 这个类,就可以进行版本控制了。

给个demo.

//构造方法, 传入引用和戳public AtomicStampedReference(V initialRef, int initialStamp)//返回引用public V getReference()//返回版本戳public int getStamp()//如果当前引用 等于 预期值并且 当前版本戳等于预期版本戳, 将更新新的引用和新的版本戳到内存public boolean compareAndSet(V   expectedReference,                                 V   newReference,                                 int expectedStamp,                                 int newStamp)//如果当前引用 等于 预期引用, 将更新新的版本戳到内存public boolean attemptStamp(V expectedReference, int newStamp)//设置当前引用的新引用和版本戳public void set(V newReference, int newStamp)


public static void main(String[] args) {        String str1 = "aaa";        String str2 = "bbb";        AtomicStampedReference reference = new AtomicStampedReference(str1,1);        reference.compareAndSet(str1,str2,reference.getStamp(),reference.getStamp()+1);        System.out.println("reference.getReference() = " + reference.getReference());        boolean b = reference.attemptStamp(str2, reference.getStamp() + 1);        System.out.println("b: "+b);        System.out.println("reference.getStamp() = "+reference.getStamp());        boolean c = reference.weakCompareAndSet(str2,"ccc",4, reference.getStamp()+1);        System.out.println("reference.getReference() = "+reference.getReference());        System.out.println("c = " + c);    }输出:reference.getReference() = bbbb: truereference.getStamp() = 3reference.getReference() = bbbc = falsec为什么输出false呢, 因为版本戳不一致啦

2jdk8对CAS的优化

由于采用这种 CAS 机制是没有对方法进行加锁的,所以,所有的线程都可以进入 increment() 这个方法,假如进入这个方法的线程太多,就会出现一个问题:每次有线程要执行第三个步骤的时候,i 的值老是被修改了,所以线程又到回到第一步继续重头再来。

而这就会导致一个问题:由于线程太密集了,太多人想要修改 i 的值了,进而大部分人都会修改不成功,白白着在那里循环消耗资源。

为了解决这个问题,Java8 引入了一个 cell[] 数组,它的工作机制是这样的:假如有 5 个线程要对 i 进行自增操作,由于 5 个线程的话,不是很多,起冲突的几率较小,那就让他们按照以往正常的那样,采用 CAS 来自增吧。但是,如果有 100 个线程要对 i 进行自增操作的话,这个时候,冲突就会大大增加,系统就会把这些线程分配到不同的 cell 数组元素去,假如 cell[10] 有 10 个元素吧,且元素的初始化值为 0,那么系统就会把 100 个线程分成 10 组,每一组对 cell 数组其中的一个元素做自增操作,这样到最后,cell 数组 10 个元素的值都为 10,系统在把这 10 个元素的值进行汇总,进而得到 100,二这,就等价于 100 个线程对 i 进行了 100 次自增操作。

总之,jdk8对于高并发的情况下,采用了类似减少锁粒度方法来提高性能。



感谢各位的阅读,以上就是"java多线程CAS的介绍"的内容了,经过本文的学习后,相信大家对java多线程CAS的介绍这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

线程 版本 问题 内存 元素 方法 系统 结果 数组 时候 机制 步骤 保证 学习 控制 更新 不同 相同 安全 操作系统 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技工作委员会副主任毛军 互联网金融监管科技数据 A股互联网金融科技公司 互联网科技媒体 云电互联网科技 互联网科技logo 互联网高科技企业 欧洲十大科技互联网公司 上海宁丞科技互联网 苏州五指互联网络科技有限 赶超互联网科技 微信多开 互联网科技和信息 无锡工业互联网科技有限公司 合肥互联网科技公司公章图片 每天都有哪些好的互联网科技 雨阳互联网卡科技有限公司 35岁以上互联网科技行业 互联网领先科技活动 网互联网络科技 互联网 科技节活动 微视频大赛 云之行互联网科技 中国互联网科技工作委员会毛军 以房互联网科技 中国科技互联网公司收入 佳云科技和互联网广告 湖北码尼互联网科技有限公司电话 华夏恒生互联网科技013171 互联网金融运营模式中的金融科技 互联网金融金融科技 现代科技的标志之一的互联网
0