Java并发基石_CAS原理实战01_CAS机制入门

news2024/12/23 1:39:28

快了,快要拿到offer了!🌹

案例引入:
开发一个网站,对访问量进行统计,用户每发送一次请求,访问量就+1,模拟有100个人同时访问,并且每个人对网站发起10次请求,所以理论上访问量应该是1000。

代码实现:

package juc.cas;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class Demo {
//        累计访问量
    static int count = 0;
    public static void request() throws InterruptedException {
//        模拟耗时5ms
        TimeUnit.MILLISECONDS.sleep(5);
//        count!=1000的原因就在于count++不是原子操作
        /**
         * 分为三步:
         * 获取count的值
         * 将count的值+1得到b
         * 将b赋值给count
         */
        count++;
    }

    public static void main(String[] args) throws InterruptedException {
//        记录开始时间
        long startTime = System.currentTimeMillis();
//        访问人数
        int threadSize = 100;
//        栅栏
        CountDownLatch countDownLatch = new CountDownLatch(threadSize);

        for (int i = 0; i < threadSize; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        for (int j = 0; j < 10; j++) {
                            request();
                        }
                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {
                        countDownLatch.countDown();
                    }
                }
            });
            thread.start();
        }
//        当100线程执行完之后,才能执行下面的代码

        countDownLatch.await();
        long endTime = System.currentTimeMillis();
        System.out.println(Thread.currentThread().getName()+"耗时:"+(endTime-startTime)+",count="+count);

    }
}

关于CountDownLatch
CountDownLatch是程序计数器,起到一个栅栏的作用,刚开始的时候为CountDownLatch设置一个值,即等待的线程数,CountDownLatch调用await()方法时,需要判断等待的线程数是否为0,如果为0才能继续执行下面的代码。

执行结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

问题出现的原因:
count++不是原子操作,实际上count++可以分为三个操作:

  • 获取count的值,记作AA=count
  • A的值+1得到B的值,即B=A+1
  • 最后再将B赋值给count

问题解决方案:
在对count++操作的时刻,只允许一个线程可以操作,而其他线程在外排队等候,只有当当前线程操作结束,其他线程才能获得对count的操作权。在Java中,这种操作可以使用synchronized关键字来完成。

代码实现:

package juc.cas;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class Demo2 {
    //        累计访问量
    static int count = 0;
    public synchronized static void request() throws InterruptedException {
//        模拟耗时5ms
        TimeUnit.MILLISECONDS.sleep(5);
        count++;
    }

    public static void main(String[] args) throws InterruptedException {
//        记录开始时间
        long startTime = System.currentTimeMillis();
//        访问人数
        int threadSize = 100;
//        栅栏
        CountDownLatch countDownLatch = new CountDownLatch(threadSize);

        for (int i = 0; i < threadSize; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        for (int j = 0; j < 10; j++) {
                            request();
                        }
                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {
                        countDownLatch.countDown();
                    }
                }
            });
            thread.start();
        }
//        当100线程执行完之后,才能执行下面的代码

        countDownLatch.await();
        long endTime = System.currentTimeMillis();
        System.out.println(Thread.currentThread().getName()+"耗时:"+(endTime-startTime)+",count="+count);

    }
}

在这里插入图片描述
加锁之后,可以保证count=1000了,但是显而易见的是,执行耗时增多了!

synchronized之后,为什么程序变慢了?
显然,加了synchronized关键字之后,保证了并发的正确性,即在同一个时刻只允许一个线程操作count,只是耗时太长了(synchronized在方法上面,如果是静态方法,那么锁住的是class)。

如何解决耗时长的问题?
只在将B赋值给count的时候加锁,分为四步:

  • 获取锁
  • 获取count的最新值,记作V
  • 判断V是否等于A,如果相等,则把B赋值给count,并返回true,否则返回false
  • 释放锁

代码实现:

package juc.cas;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class Demo3 {
    //        累计访问量
    volatile static int count = 0;
    public  static void request() throws InterruptedException {
//        模拟耗时5ms
        TimeUnit.MILLISECONDS.sleep(5);

        int expectCount;
        while (!compareAndSwap(expectCount=getCount(),expectCount+1)){

        }
    }

    /**
     *
     * @param expectCount 期望值
     * @param newCount 需要给count赋值的新值
     * @return 成功返回true,否则返回false
     */
    public static synchronized boolean compareAndSwap(int expectCount,int newCount){
//        判断count和expectCount是否相等,如果相等,则将expectCount赋值给count
        if (getCount()==expectCount){
            count = newCount;
            return true;
        }

        return false;
    }

    public static int getCount(){return count;}

    public static void main(String[] args) throws InterruptedException {
//        记录开始时间
        long startTime = System.currentTimeMillis();
//        访问人数
        int threadSize = 100;
//        栅栏
        CountDownLatch countDownLatch = new CountDownLatch(threadSize);

        for (int i = 0; i < threadSize; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        for (int j = 0; j < 10; j++) {
                            request();
                        }
                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {
                        countDownLatch.countDown();
                    }
                }
            });
            thread.start();
        }
//        当100线程执行完之后,才能执行下面的代码

        countDownLatch.await();
        long endTime = System.currentTimeMillis();
        System.out.println(Thread.currentThread().getName()+"耗时:"+(endTime-startTime)+",count="+count);

    }
}

在这里插入图片描述

文章参考:小刘老师讲源码

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nginx+Tomcat负载均衡、动静分离群集

Tomcat优化 Tomcat是java开发的应用程序&#xff0c;作用&#xff1a;作为web服务器处理html页面&#xff0c;但能力一般&#xff0c;更多的用于jsp、servlet容器处理java开发的jsp动态页面。 组织架构&#xff1a;连接器 、 容器 连接器&#xff1a;暴露端口&#xff0c;接…

云表:无代码“打破”工业软件开发壁垒,数字化只需"画表格"

无代码已成为新兴趋势 近年来&#xff0c;在如火如荼的制造业数字化转型浪潮中&#xff0c;“无代码开发”也因其敏捷、易用的独有特性&#xff0c;助力企业实现数字化应用的快速开发与落地&#xff0c;使得数据业务价值在企业级场景下释放&#xff0c;受到市场广泛关注。据国际…

003 常用组件开发使用

目录 一.基础组件 Blank:填充控件 Button&#xff1a;按钮 ButtonType枚举说明 Text&#xff1a;文本显示 QRCode 二.常用布局 线性布局&#xff08;Row和Column&#xff09; 层叠布局 弹性布局&#xff08;Flex&#xff09; 一.基础组件 Blank:填充控件 这个是鸿蒙…

涨薪5k,100多天从功能测试进阶自动化测试,我整理的超全学习指南

个人简介 学渣一枚&#xff0c;2017年6月某大专学校毕业&#xff0c;从事功能测试已经4年&#xff0c;最初毕业是从事了一份销售的工作&#xff0c;工资当时好像是3k&#xff0c;可能也是我个人的原因不适合销售&#xff0c;后来在朋友的介绍下转行到了测试行业&#xff0c;转…

访问者模式解读

目录 问题引进 访问者模式基本介绍 基本介绍 访问者模式的原理类图 对原理类图的说明 访问者模式应用实例 思路分析和图解(类图) 代码实战 应用案例的小结 访问者模式的注意事项和细节 优点 问题引进 测评系统的需求 1) 将观众分为男人和女人&#xff0c;对歌手进行…

FPGA基于XDMA实现PCIE X8通信方案测速 提供工程源码和QT上位机程序和技术支持

目录 1、前言2、我已有的PCIE方案3、PCIE理论4、总体设计思路和方案5、vivado工程详解6、驱动安装7、QT上位机软件8、上板调试验证9、福利&#xff1a;工程代码的获取 1、前言 PCIE&#xff08;PCI Express&#xff09;采用了目前业内流行的点对点串行连接&#xff0c;比起 PC…

Ceph手动部署(开发版本)

手动部署 监视器引导管理器守护程序配置添加 OSD 简写形式长格式添加 MDS总结在 FreeBSD 上手动部署 FreeBSD 上的 Disklayout 配置监视器引导添加 OSD 长格式添加 MDS总结 手动部署 所有 Ceph 集群至少需要一个显示器&#xff0c;并且至少需要与 存储在群集上的对象的副本…

Hacked某安汽车车机系统

很久之前尝试对某安汽车的车机系统进行渗透测试&#xff0c;但是却卡在入口无法进入&#xff0c;尝试暴力破解但是字典不够强大&#xff0c;没能成功。前段时间看到了绿盟科技博客的《新型车机&#xff0c;如何攻防&#xff1f;》感觉有点熟悉&#xff0c;再次探索发现可以获得…

京东商品评论数据爬虫,包含对数据的采集、清洗、可视化、分析等过程,作为数据库课程。

感谢大家的star和fork&#xff0c;为了感谢大家的关注&#xff0c;特意对代码进行了优化&#xff0c;对最新的url格式进行了更新&#xff0c;减少了一些冗余的参数&#xff0c;希望能够帮助大家入门爬虫&#xff0c;已经爬好的京东的商品评论数据已经存储在data目录下&#xff…

软件测试标准GB/T 25000.51-2016中的八大软件质量特性

GB/T25000标准由下图所示的21个部分组成&#xff0c;其中GB/T 25000.10和GB/T 25000.51是建立软件测试技术体系可以参考的部分&#xff0c;GB/T 25000.51尤为重要。 GB/T 25000标准总标题&#xff08;21个部分&#xff09; GB/T 25000.51标准pdf封面 GB/T 25000.51-2016 《系统…

现在有t1,t2,t3三个线程,实现t1,t2线程同步执行,然后再执行t3线程,使用Java实现该程序

目录 1、利用CountDownLatch 2、利用Future 最近在面试的时候&#xff0c;经常遇到这个题目&#xff0c;首先从题目上看&#xff0c;就知道考察的是多线程方面知识&#xff0c;我第一次看到这个题目的时候&#xff0c;就想到了使用CountDownLatch这个计数器来实现&#xff0c…

AUTOSAR网络管理

功能说明 目前车辆上ECU的数目越来越多&#xff0c;不同功能的ECU对电源有不同的要求&#xff0c;在点火钥匙打到OFF档&#xff08;KL15停止供电&#xff09;之后&#xff0c;有的ECU&#xff08;如座椅模块&#xff09;允许直接断电&#xff0c;有的ECU&#xff08;如空调模块…

浅谈操作系统OS与计算机软硬件体系结构,自顶贯穿性与行为回归硬件性

操作系统OS与计算机软硬件体系结构 使计算机更好用! 这是操作系统的根本要义!! 操作系统这个概念基本上以后会讲一路的&#xff0c;今天的话就基本上讲一下轻量化的概念。所以操作系统到底是什么&#xff1f;操作系统首先是软件&#xff0c;那它是一款什么软件呢&#xff1f…

Java读取文件方式

IO流读取 文本内容 按行读取文件内容 指定编码格式&#xff08;推荐&#xff09; public static void main(String[] args) throws UnsupportedEncodingException {read("D:\\test.txt");}public static void read(String path) {BufferedReader reader null;try …

Spring Security 01 整体架构

目录 认证 AuthenticationManager ProviderManager AuthenticationProvider Authentication SecurityContextHolder 授权 AccessDecisionManager AccessDecisionVoter RoleVoter AuthenticatedVoter Custom Voters ConfigAttribute 在SpringSecurity的架构中&…

Linux如何使用宝塔面板搭建网站和内网穿透实现公网访问

文章目录 前言1. 环境安装2. 安装cpolar内网穿透3. 内网穿透4. 固定http地址5. 配置二级子域名6. 创建一个测试页面 转载自远程内网穿透的文章&#xff1a;Linux使用宝塔面板搭建网站&#xff0c;并内网穿透实现公网访问 前言 宝塔面板作为简单好用的服务器运维管理面板&#…

Flink从入门到精通之-06Flink 中的时间和窗口

Flink从入门到精通之-06Flink 中的时间和窗口 我们已经了解了基本 API 的用法&#xff0c;熟悉了 DataStream 进行简单转换、聚合的一些操作。除此之外&#xff0c;Flink 还提供了丰富的转换算子&#xff0c;可以用于更加复杂的处理场景。 在流数据处理应用中&#xff0c;一个…

NM储存卡数据丢失怎么办?四招数据恢复宝典

NM卡像其他类型的存储设备一样&#xff0c;也有可能因为各种原因导致数据丢失&#xff0c;比如误删除、格式化、病毒感染等。因此&#xff0c;在使用NM卡时&#xff0c;仍需注意数据备份和安全性&#xff0c;以避免面临重要数据丢失风险。如果不幸发生了数据丢失&#xff0c;应…

python中unexpected indent报错的解决办法

python中unexpected indent报错的解决办法 在我们初步学习pyton的时候&#xff0c;由于对python语言的学习掌握不充分&#xff0c;则会导致所编写的代码&#xff0c;运行时候报错。比如&#xff0c;容易报错的unexpected indent问题&#xff0c;下面举例说明问题。 1.举例&am…

Linux虚拟机中安装jdk的两种方法:

方法一&#xff1a;手动安装 1. 使用FinalShell自带的上传工具将jdk的二进制发布包上传到Linux 上传位置如图&#xff08;底栏可以在图中的向下箭头位置自行打开与关闭&#xff09;&#xff1a; 注&#xff1a;默认上传地址为图片左侧的工作地址 2. 解压安装包&#xff0c;…