小白都能看懂的CAS基本原理与实战应用指南

简介: 小白都能看懂的CAS基本原理与实战应用指南

引言

最近有个读者问我什么是CAS,今天了不起来聊聊CAS(Compare And Swap)这个概念。

它在日常编程中非常有用,特别是在多线程编程中。

本篇文章将从原理介绍、源码分析、实战应用等方面进行阐述。

一、CAS原理介绍

CAS(Compare And Swap)是一种用于实现无锁并发算法的技术。

大多数时候它也还有其他叫法:无锁优化、自旋、乐观锁

它的核心思想是:通过比较当前需要修改的值与预期原来的值,如果相等,则使用新值进行替换。

这个过程是原子性的,它底层是靠C语言依赖的操作系统的原子操作来保证原子性的,即在这个过程中不会被其他线程打断。

在Java中,CAS操作主要是通过 java.util.concurrent.atomic包中的原子类来实现的,如 AtomicIntegerAtomicLong等。

原理示例

假设有一个整数变量 count,初始值为0。现在有两个线程A和B同时对 count进行加1操作。使用CAS操作可以确保 count最终的值为2。

  1. 线程A读取 count的值,得到0。
  2. 线程B读取 count的值,得到0。
  3. 线程A将 count的值与预期值0进行比较,相等,则将 count的值更新为1。
  4. 线程B将 count的值与预期值0进行比较,不相等(因为已经被线程A更新为1),则重试。
  5. 线程B重新读取 count的值,得到1。
  6. 线程B将 count的值与预期值1进行比较,相等,则将 count的值更新为2。

最终,count的值为2,实现了无锁并发操作。

二、源码分析

java.util.concurrent.atomic.AtomicInteger为例,这个类提供了一个原子的整数值,可以用于实现无锁的整数操作。我们来看一下它的 compareAndSet方法的源码:

public final boolean compareAndSet(int expect, int update){
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

这里的 unsafe是一个 sun.misc.Unsafe类型的对象,它提供了底层的CAS操作。valueOffset是一个静态常量,表示 AtomicInteger内部的 value字段的内存偏移量。expect是预期值,update是新值。这个方法会返回一个布尔值,表示操作是否成功。

小结:

atomicXXX类

  • Compare and Set/Swap 比较并且设定
  • cas(V,Expected,NewValue)
    if V == EV == New otherwise try again or fail
  • CPU原语支持 atomic底层调用到UnSafe这个方法,三个参数V就是当前版本值,Expected期望值,NewValue赋予的新值。
    只有当V等于E的时候才将新的值赋给这个变量,又因为它是原语支持是CPU级别的,是一个原子操作,所以在设值时不会有其他线程来插队设值。

三、实战应用

我们来看一个实际的例子,如何使用 AtomicInteger实现一个无锁的计数器。

public class Counter {
    /*volatile*/ //int count1 = 0;
    AtomicInteger count = new AtomicInteger(0);
    /*synchronized*/ void m() {
        for (int i = 0; i < 10000; i++)
            //if count1.get() < 1000
            count.incrementAndGet(); //count1++
    }
    public static void main(String[] args) {
        Counter t = new Counter();
        List<Thread> threads = new ArrayList<Thread>();
        for (int i = 0; i < 10; i++) {
            threads.add(new Thread(t::m, "thread-" + i));
        }
        threads.forEach((o) -> o.start());
        threads.forEach((o) -> {
            try {
                o.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println(t.count);
    }
}

在这个例子中,我们创建了一个 Counter类,内部使用 AtomicInteger实现了无锁的计数,然后我们创建了10个线程,分别对计数器进行增加操作。最后我们输出计数器的值,可以看到它确实是10000,证明我们的无锁计数器是正确的。

四、CAS在日常中的应用场景

在实际开发中,我们可能会遇到以下几种使用CAS的场景:

  1. 无锁计数器 :如上面的例子所示,我们可以使用CAS实现一个高效的无锁计数器,避免了使用同步锁带来的性能开销。
  2. 单例模式 :我们可以使用CAS实现一种线程安全的单例模式,确保在多线程环境下只创建一个实例。
  3. 并发容器 :在实现高性能的并发容器时,如 ConcurrentHashMap,我们可以使用CAS操作来实现无锁或低锁的数据结构更新。
  4. 多线程并发任务 :在多线程并发执行任务时,我们可以使用CAS操作来确保任务状态的正确更新,例如实现一个无锁的任务分发器。

五、ABA问题

如果另一个线程修改V值,假设原来是A,先修改成B,再修改回成A。

当前线程的CAS操作无法分辨当前V值是否发生过变化,这个就是ABA问题。

解决:

  • 在CAS的时候加版本号,每次操作比较下版本号
  • 加 version
  • A 1.0
  • B 2.0
  • A 3.0
  • cas(version)
  • 原子类 AtomicStampedReference解决ABA问题
  • ABA问题重要不?如果是基本数据类型结果没影响,如果是引用对象就不好说了,比如你的女朋友和你复合,前面经过了多少个其他XXX,你觉得有影响不?

六、总结

了不起带着大家从原理介绍、源码分析、实战应用等方面讲解了CAS的相关知识。

通过本文的学习,相信你们已经对CAS有了一定的了解,掌握了如何在实际开发中应用CAS来解决并发问题。

当然,CAS并不是万能的,它也有一定的局限性,例如ABA问题。

在实际开发中,我们需要根据具体场景选择合适的并发策略。

相关文章
|
存储 编译器 C语言
[Eigen中文文档] 对未对齐数组断言的解释
本文将解释程序因断言失败而终止的问题。
648 0
|
Oracle 关系型数据库 Linux
redhat kmod
ASMLib Downloads for Red Hat Enterprise Linux
6060 4
|
弹性计算 网络安全 数据安全/隐私保护
80端口备案,有么有详细教程怎么去备案呀?
80端口备案,有么有详细教程怎么去备案呀?
4823 0
|
11月前
|
前端开发 JavaScript NoSQL
如何开发一套绩效管理(KPI)系统?(附架构图+流程图+代码参考)
本文介绍了如何构建科学有效的绩效管理(KPI)系统,帮助企业提升组织效率与员工成长。内容涵盖系统架构设计、功能模块开发、业务流程落地及实操技巧,提供架构图、流程图和核心代码参考,助力快速实现企业绩效管理数字化。
|
数据采集 存储 XML
Python爬虫:深入探索1688关键词接口获取之道
在数字化经济中,数据尤其在电商领域的价值日益凸显。1688作为中国领先的B2B平台,其关键词接口对商家至关重要。本文介绍如何通过Python爬虫技术,合法合规地获取1688关键词接口,助力商家洞察市场趋势,优化营销策略。
|
Java 应用服务中间件 API
Servlet开发流程 (里面有Idea项目添加Tomcat依赖详细教程)
本文详细介绍了Servlet的开发流程,包括在IntelliJ IDEA中添加Tomcat依赖的详细教程。通过上述步骤,开发者可以快速搭建并运行一个基本的Servlet应用,理解并掌握Servlet的开发流程对于Java Web开发至关重要。希望本文能够帮助开发者顺利进行Servlet开发,提高工作效率。
1752 78
|
11月前
|
机器学习/深度学习 搜索推荐 算法
基于大数据的电影点评与推荐
本系统基于大数据与人工智能技术,构建电影点评与推荐平台,提供个性化电影推荐、用户点评及社交互动功能。通过分析用户行为与电影属性,优化推荐算法,提升用户体验与满意度,促进电影产业发展与市场活跃。
基于大数据的电影点评与推荐
|
存储 消息中间件 NoSQL
Redis从入门到精通之底层数据结构基数树和listpacks详解
Redis是一种内存数据库,其高性能的基础来自于其底层的数据结构的设计。在Redis中,数据结构是一种抽象和具体的概念,可以看作是Redis提供的一些操作的实现方式。Redis支持多种数据结构,如字符串、列表、哈希、集合、有序集合等。其中,底层的数据结构包括基数树和listpacks,本文将对这两种数据结构进行详细的介绍。
968 0
Redis从入门到精通之底层数据结构基数树和listpacks详解
|
存储 应用服务中间件 nginx
nginx反向代理bucket目录配置
该配置实现通过Nginx代理访问阿里云OSS存储桶中的图片资源。当用户访问代理域名下的图片URL(如 `http://代理域名/123.png`)时,Nginx会将请求转发到指定的OSS存储桶地址,并重写路径为 `/prod/files/2024/12/12/123.png`。
720 5
|
算法 安全 数据安全/隐私保护
TLS 1.3 相比 TLS 1.2 在性能上有哪些具体的提升?
【10月更文挑战第4天】TLS 1.3 相比 TLS 1.2 在性能上有哪些具体的提升