【并发知识点】CAS的实现原理及应用

系列文章目录

AQS的实现原理及应用
CAS的实现原理及应用

在这里插入图片描述


文章目录

  • 系列文章目录
  • 前言
  • 1、CAS的概念
  • 2、CAS的实现原理
  • 3、单JVM内锁CAS实现
    • 3.1、效果
  • 4、模拟赛龙舟比赛


前言

本章节介绍CAS概念、实现原理,并通过java代码应用,最终模拟赛龙舟比赛。

1、CAS的概念

CAS的全称为:CompareAndSwap,直译为对比和交换。
CAS实际是普遍处理器都支持的一条指令,这条指令通过判断当前内存值V、旧的预期值A、即将更新的值B是否相等来对比并设置新值,从而实现变量的原子性。
Synchronized会线程阻塞称为悲观锁,CAS不会使线程阻塞称为乐观锁。悲观锁其他没有获取锁的线程是不会执行代码的,而乐观锁是可以使多个线程同时访问代码,可是会判断是否有更新决定是否重新执行。

2、CAS的实现原理

CAS的原理:通过三个参数,当前内存的变量值V、旧的预期值A、即将更新的值B。通过判断是V和A是否相等查看当前变量值是否被其他线程改变,如果相等则变量没有被其他线程更改,就把B值赋予V;如果不相等则做自旋操作。
举例:假设i的初始值为0,现两线程分别执行i++操作,看看CAS会如何执行:

1线程:A = 0,B = 1
2线程:A = 0,B = 1

此时两个线程的旧的期望值A、更新的B值都是一样的
假设1线程先执行,线程1从内存中拿到i的值V,此时V等于0,刚好和旧的期望值A相等,就把更新的值B赋值到内存i值V中。
2线程执行时,此时拿到内存的V值为1,和旧的预期值0不想等,则不做更新B的赋值操作,通过自旋把旧的预期值A=V,并再次确定CAS指令。
在这里插入图片描述

JDK提供的原子操作类就是基于CAS来实现原子性,比如:AtomicInteger、AtomicIntegerArray、AtomicDouble、AtomicBoolean等

3、单JVM内锁CAS实现

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/***
 * @title AtomicExample
 * @desctption CAS
 * @author Kelvin
 * @create 2023/6/14 17:08
 **/
public class AtomicExample {
    private static Map<String, Boolean> map = new HashMap<>();

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //未加锁
        map.put("lock", true);
        for (int i = 0; i < 20; i++) {
            int finalI = i;
            executorService.submit(
                    () -> {
                        boolean isRotate = true;
                        while (isRotate) {
                            boolean vLock = map.get("lock");
                            if( vLock ) {
                                boolean aLock = map.get("lock");
                                if( vLock == aLock ) {
                                    //执行业务逻辑
                                    //加锁
                                    map.put("lock", false);
                                    System.out.println( "执行业务逻辑, i: " + finalI);
                                    try {
                                        Thread.sleep(2000);
                                    } catch (InterruptedException e) {
                                        throw new RuntimeException(e);
                                    }
                                    isRotate = false;
                                    //释放锁
                                    map.put("lock", true);
                                } else {
                                    System.out.println("自旋,重新获取锁!");
                                    continue;
                                }
                            }
                        }
                    }
            );
        }

        Thread.sleep(20 * 1000);
        System.out.println("end");
        executorService.shutdown();
        
    }
}

3.1、效果

执行业务逻辑, i: 1
执行业务逻辑, i: 5
自旋,重新获取锁!
自旋,重新获取锁!
自旋,重新获取锁!
自旋,重新获取锁!
自旋,重新获取锁!
自旋,重新获取锁!
执行业务逻辑, i: 4
自旋,重新获取锁!
自旋,重新获取锁!
执行业务逻辑, i: 10
自旋,重新获取锁!
自旋,重新获取锁!
执行业务逻辑, i: 9
执行业务逻辑, i: 2
自旋,重新获取锁!
执行业务逻辑, i: 3
自旋,重新获取锁!
执行业务逻辑, i: 0
执行业务逻辑, i: 12
执行业务逻辑, i: 11
执行业务逻辑, i: 8
执行业务逻辑, i: 6
执行业务逻辑, i: 7
执行业务逻辑, i: 14
自旋,重新获取锁!
执行业务逻辑, i: 15
执行业务逻辑, i: 16
执行业务逻辑, i: 18
自旋,重新获取锁!
自旋,重新获取锁!
执行业务逻辑, i: 17
执行业务逻辑, i: 19
执行业务逻辑, i: 13
end

4、模拟赛龙舟比赛

在这个程序中,我们将使用Java的AtomicInteger来实现一个裁判的计时器,模拟一个赛龙舟比赛中多支队伍竞争的场景。每个队伍都会在启动时创建一个独立的线程,并在程序中使用LockFreeQueue来模拟龙舟的运动。同时,程序中还会使用CountDownLatch来控制所有龙舟同时开始比赛,并使用CyclicBarrier来模拟所有队伍完成比赛后的庆祝活动。

import java.util.Random;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class DragonBoatRace {
    private static final int TEAM_NUM = 4; // 参赛队伍数
    private static final int BOAT_NUM = 1; // 龙舟数量
    private static final Random random = new Random();
    private static final AtomicInteger time = new AtomicInteger(0);
    private static final CountDownLatch startLatch = new CountDownLatch(TEAM_NUM);
    private static final CyclicBarrier finishBarrier = new CyclicBarrier(TEAM_NUM + 1);
    private static final LockFreeQueue<Integer> queue = new LockFreeQueue<>();

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(TEAM_NUM);
        for (int i = 0; i < TEAM_NUM; i++) {
            executorService.submit(new Team(i + 1));
        }
        startLatch.await(); // 等待所有队伍准备就绪
        System.out.println("比赛开始!");
        for (int i = 0; i < BOAT_NUM; i++) {
            new Thread(new Boat(i + 1)).start();
        }
        System.out.println("龙舟已经准备好!");
        finishBarrier.await(); // 等待所有队伍完成比赛
        System.out.println("所有队伍完成比赛,开始庆祝!");
        executorService.shutdown(); // 关闭线程池
    }

    static class Team implements Runnable {
        private final int teamId;

        public Team(int teamId) {
            this.teamId = teamId;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(1000 * teamId); // 模拟队伍准备时间
                System.out.println("队伍" + teamId + "已准备就绪!");
                startLatch.countDown(); // 准备就绪,计数器减一
                queue.add(1); // 龙舟前进一步
                while (time.get() < 2000) { // 等待龙舟到达终点
                    Thread.yield();
                }
                System.out.println("队伍" + teamId + "已完成比赛,用时" + time.get() + "ms!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                try {
                    finishBarrier.await(); // 等待其他队伍完成比赛
                    System.out.println("队伍" + teamId + "正在庆祝!");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class Boat implements Runnable {
        private final int boatId;

        public Boat(int boatId) {
            this.boatId = boatId;
        }

        @Override
        public void run() {
            while (!queue.isEmpty()) { // 龙舟前进,直到到达终点
                if (queue.poll() != null) {
                    time.addAndGet(random.nextInt(10) + 1); // 龙舟前进一步,用时1~10ms
                }
            }
            try {
                finishBarrier.await(); // 等待所有队伍完成比赛
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    static class LockFreeQueue<E> {
        private static class Node<E> {
            final E item;
            volatile Node<E> next;

            Node(E item) {
                this.item = item;
            }
        }

        private volatile Node<E> head;
        private volatile Node<E> tail;

        public boolean isEmpty() {
            return head == null;
        }

        public void add(E item) {
            Node<E> node = new Node<>(item);
            while (true) {
                Node<E> last = tail;
                Node<E> next = last == null ? head : last.next;
                if (last == tail) {
                    if (next == null) {
                        if (compareAndSetNext(last, null, node)) {
                            compareAndSetTail(last, node);
                            return;
                        }
                    } else {
                        compareAndSetTail(last, next);
                    }
                }
            }
        }

        public E poll() {
            while (true) {
                Node<E> first = head;
                Node<E> last = tail;
                Node<E> next = first == null ? null : first.next;
                if (first == head) {
                    if (first == last) {
                        if (next == null) {
                            return null;
                        }
                        compareAndSetTail(last, next);
                    } else {
                        E item = next.item;
                        if (compareAndSetHead(first, next)) {
                            return item;
                        }
                    }
                }
            }
        }

        private boolean compareAndSetHead(Node<E> expect, Node<E> update) {
            return unsafe.compareAndSwapObject(this, headOffset, expect, update);
        }

        private boolean compareAndSetTail(Node<E> expect, Node<E> update) {
            return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
        }

        private boolean compareAndSetNext(Node<E> node, Node<E> expect, Node<E> update) {
            return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
        }

        private static final sun.misc.Unsafe unsafe;
        private static final long headOffset;
        private static final long tailOffset;
        private static final long nextOffset;

        static {
            try {
                unsafe = getUnsafe();
                Class<?> k = LockFreeQueue.class;
                headOffset = unsafe.objectFieldOffset(k.getDeclaredField("head"));
                tailOffset = unsafe.objectFieldOffset(k.getDeclaredField("tail"));
                nextOffset = unsafe.objectFieldOffset(Node.class.getDeclaredField("next"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }

        private static sun.misc.Unsafe getUnsafe() throws Exception {
            Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            return (sun.misc.Unsafe) field.get(null);
        }
    }
}

在这个程序中,我们模拟了4个队伍参加赛龙舟比赛。每个队伍都在启动时创建一个独立的线程,并在比赛前等待1-4s的准备时间。当所有队伍都准备就绪后,裁判发出比赛开始信号,龙舟开始前进。程序中使用LockFreeQueue来模拟龙舟的运动,每次龙舟前进一步,用时1~10ms。当某个队伍完成比赛后,程序会使用CyclicBarrier来等待其他队伍完成比赛,并且进行庆祝活动。

总之,基于CAS的Java多线程程序可以很好地模拟赛龙舟比赛中的多支队伍竞争的场景。通过使用Java的AtomicInteger和LockFreeQueue等基于CAS的同步机制,我们可以实现高效的线程协作和同步操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/32107.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【spring cloud学习】2、Eureka服务注册与发现

前言 一套微服务架构的系统由很多单一职责的服务单元组成&#xff0c;而每个服务单元又有众多运行实例。由于各服务单元颗粒度较小、数量众多&#xff0c;相互之间呈现网状依赖关系&#xff0c;因此需要服务注册中心来统一管理微服务实例&#xff0c;维护各服务实例的健康状态…

【HTML】常用标签

文章目录 1.标题字标签h1-h62.段落标签p3.换行标签br4.格式化标签5.图片标签6.超链接标签a7.表格标签单元格合并行合并列合并 8.无序列表9.有序列表10.自定义列表11.表单标签11.1 form标签11.2 表单控件11.2.1 input标签11.2.2 label标签11.2.3 select标签11.2.4 textarea标签 …

外网SSH远程连接linux服务器「cpolar内网穿透」

文章目录 视频教程1. Linux CentOS安装cpolar2. 创建TCP隧道3. 随机地址公网远程连接4. 固定TCP地址5. 使用固定公网TCP地址SSH远程 转载自内网穿透工具的文章&#xff1a;无公网IP&#xff0c;SSH远程连接Linux CentOS服务器【内网穿透】 本次教程我们来实现如何在外公网环境下…

两阶段目标检测指南:R-CNN、FPN、Mask R-CNN

动动发财的小手&#xff0c;点个赞吧&#xff01; Source[1] 多阶段&#xff08;Two-stage&#xff09;物体检测 计算机视觉中最基本和最广泛研究的挑战之一是目标检测。该任务旨在在给定图像中绘制多个对象边界框&#xff0c;这在包括自动驾驶在内的许多领域非常重要。通常&am…

2022年长三角高校数学建模竞赛B题齿轮箱故障诊断解题全过程文档及程序

2022年长三角高校数学建模竞赛 B题 齿轮箱故障诊断 原题再现&#xff1a; 齿轮箱是用于增加输出扭矩或改变电机速度的机械装置&#xff0c;被广泛应用于如汽车、输送机、风机等机械设备中。它由两个或多个齿轮组成&#xff0c;其中一个齿轮由电机驱动。电机的轴连接到齿轮箱的…

SpringMvc入门

SpringMvc用来代替展示层Servlet&#xff0c;均属于Web层开发技术 Servlet是如何工作的 1、导入Servlet依赖坐标 2、创建一个Servlet接口实现类&#xff0c;重写其中的所有方法 3、在Servlet实现类上加上WebServlet注解&#xff0c;用来配置Servlet访问路径 4、启动Tomca…

总结906

学习目标&#xff1a; 月目标&#xff1a;6月&#xff08;线性代数强化9讲&#xff0c;背诵15篇短文&#xff0c;考研核心词过三遍&#xff09; 周目标&#xff1a;线性代数强化3讲&#xff0c;英语背3篇文章并回诵&#xff0c;检测 每日规划 今日已做&#xff1a; 1.回环背诵…

详解Hystrix

目录 1.微服务中的容错 1.1.服务雪崩 1.2.解决办法 2.hystrix 2.1.概述 2.2.项目结构及依赖 2.3.代码示例 2.3.1.注册中心 2.3.2.服务调用者 2.3.3.服务提供者 2.4.服务降级 2.4.1.单点响应 2.4.2.默认响应 2.4.3.前置响应 2.5.服务熔断 2.5.1.概述 2.5.2.使用…

centos 安装 nginx

1.下载nginx安装包 wget -c https://nginx.org/download/nginx-1.24.0.tar.gz 下载到了当前目录下 2.解压安装包 解压后的结果 3.安装依赖 yum -y install gcc gcc-c make libtool zlib zlib-devel openssl openssl-devel pcre pcre-devel 4. ./configure --prefix/usr/lo…

【Linux】深入理解文件系统

系列文章 收录于【Linux】文件系统 专栏 关于文件描述符与文件重定向的相关内容可以移步 文件描述符与重定向操作。 可以到 浅谈文件原理与操作 了解文件操作的系统接口。 想深入理解文件缓冲区还可以看看文件缓冲区。 目录 系列文章 磁盘 结构介绍 定位数据 抽象管理…

高速电路设计系列分享-电源噪声分析

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 提示&#xff1a;这里可以添加技术概要 例如&#xff1a; 当今许多应用都要求高速采样模数转换器&#xff08;ADC)具有12位或以上的分辨率&#xff0c;以便用户能够进行更精确的系统测量。然而&#xff0c;更高分辨率…

【机器学习】机器故障的二元分类模型-Kaggle竞赛

竞赛介绍 数据集描述 本次竞赛的数据集&#xff08;训练和测试&#xff09;是从根据机器故障预测训练的深度学习模型生成的。特征分布与原始分布接近&#xff0c;但不完全相同。随意使用原始数据集作为本次竞赛的一部分&#xff0c;既可以探索差异&#xff0c;也可以了解在训…

使用CloudOS快速实现K8S容器化部署

关于容器技术 容器技术&#xff08;以docker和Kubernetes为代表&#xff09;呱呱坠地到如今&#xff0c;在国内经历了如下3个阶段&#xff1a; 婴儿期&#xff1a;2014-2016年的技术探索期&#xff1b; 少儿期&#xff1a;2017-2018年的行业试水期&#xff1b; 少年期&…

【Django | 爬虫 】收集某吧评论集成舆情监控(附源码)

&#x1f935;‍♂️ 个人主页: 计算机魔术师 &#x1f468;‍&#x1f4bb; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 文章目录 一、爬取帖子、二级评论二、构建数据表三、并入项目1. spider代码2. view视图代码3. 优化后台界面3. urls路由 四、定…

自然语言处理: 第三章NPLM(Neural Probabilistic Language Mode)

理论基础 NPLM的全称是"Neural Probabilistic Language Model"&#xff0c;即神经概率语言模型。这是一种基于神经网络的语言模型&#xff0c;用于生成自然语言文本。最早是由Bengio 在2003年的A Neural Probabilistic Language Model一文中提出来的&#xff0c; NP…

【永久服务器】EUserv

1. 请先自行准备网络&#xff08;我用的伦敦还可以&#xff09;、以及visa卡&#xff0c;淘宝可以代付&#xff0c;我总共花了97人民币&#xff08;10.94欧代付费&#xff09; 现在只能申请一台&#xff0c;多了会被删除&#xff0c;也就是两欧元&#xff0c;然后选择visa卡 选…

MySQL(六):基本的SELECT语句

基本的SELECT语句 前言一、SELECT...二、SELECT ... FROM三、列的别名四、去除重复行五、空值参与运算六、着重号七、查询常数八、显示表结构九、过滤数据 前言 本博主将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识&#xff0c;有兴趣的小伙伴可以关注博主&#…

AI2:仅凭开源数据,可达ChatGPT 83%表现

夕小瑶科技说 原创 作者 | Python ChatGPT强大的性能让人爱不释手&#xff0c;ChatGPT迟迟不开源让人恨得牙根痒痒。那仅通过开源数据&#xff0c;能够取得怎样的效果呢&#xff1f;近期&#xff0c;AI2的一篇论文显示&#xff0c;最好的65B规模的模型能够达到ChatGPT表现的8…

【MySQL事务】保证数据完整性的利器

1、事务的认识 事务&#xff1a;事务就是将多个SQL给打包在一起&#xff0c;组成一个整体。组成这个整体的各个SQL&#xff0c;要么全部成功&#xff0c;要么全部失败。 举例说明&#xff1a; 情人节到了&#xff0c;滑稽老铁打算给他女朋友小美发给红包&#xff0c;但是他又害…

Spring 是什么?IoC 和 DI的区别

1. Spring 是什么?2. IoC是什么&#xff1f; 2.DI概念说明 1. Spring 是什么? 我们通常讲的Spring指的是Spring Framework(Spring框架),它是一个开源的框架,有着活跃而庞大的社区,这也是它之所谓经久不衰的原因。官方的解读是:Spring官网 翻译过来就是:Spring使Java编程对每…