java JUC并发编程 第六章 CAS

news2024/11/23 9:09:23

系列文章目录

第一章 java JUC并发编程 Future: link
第二章 java JUC并发编程 多线程锁: link
第三章 java JUC并发编程 中断机制: link
第四章 java JUC并发编程 java内存模型JMM: link
第五章 java JUC并发编程 volatile与JMM: link
第六章 java JUC并发编程 CAS: link


文章目录

  • 系列文章目录
  • 1 概述
    • 1.1 没有CAS之前多线程环境不使用原子类保证线程安全i++(基本数据类型)
    • 1.2 使用cas之后多线程环境使用原子类保证线程安全i++
  • 2 CAS是什么
    • 2.1 CAS原理
      • 2.1.1 例
      • 2.1.2 unasfe类
      • 2.1.3 硬件级别保证
      • 2.1.4 UnSafe的理解
      • 2.1.5 UnSafe底层原理
        • 2.1.5.1 getAndIncrement()
        • 2.1.5.2 native修饰的方法代表是底层方法
        • 2.1.5.3 compxchg
        • 2.1.5.4 在不通的操作系统下会调用不同的cmpxchg重载函数
    • 2.2 总结
  • 3 原子引用
    • 3.1 Atomiclnteger<T> 其他原子类型
  • 4 CAS与自旋锁
    • 4.1 自己实现自旋锁
  • 5 CAS的缺点
    • 5.1 循环时间长导致cpu开销过大
    • 5.2 引出ABA问题
      • 5.2.1 AtomicStampedReference简单case
      • 5.2.2 AtomicInteger BAB问题复现
      • 5.2.3 AtomicStampedReference 版本号机制避免ABA


1 概述

了解原子类:java.util.concurrent.atomic 下面所有相关的类和api的调用

1.1 没有CAS之前多线程环境不使用原子类保证线程安全i++(基本数据类型)

在这里插入图片描述

1.2 使用cas之后多线程环境使用原子类保证线程安全i++

在这里插入图片描述
在这里插入图片描述
两者的区别AtomicInteger要比加了synchronized的重量级锁性能更好。
类似乐观锁

2 CAS是什么

在这里插入图片描述

2.1 CAS原理

在这里插入图片描述
在这里插入图片描述

2.1.1 例

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(5);
        //预期是5的时候才替换成2022
        System.out.println(atomicInteger.compareAndSet(5,2022)+" :"+ atomicInteger.get());
    }
}

在这里插入图片描述

2.1.2 unasfe类

this:当前对象
valueoffset:当前对象内存地址的偏移量
expect:期望值
update:更新值
在这里插入图片描述

2.1.3 硬件级别保证

在这里插入图片描述
AtomicInteger底层调用的是Unsafe类
在这里插入图片描述

2.1.4 UnSafe的理解

在这里插入图片描述
在这里插入图片描述
i++是线程不安全的,那atomiclnteger.getAndIncrement()就可以么
在这里插入图片描述
在这里插入图片描述
var1:this(当前对象)
var2:valueoffset
var5:当前对象最新值
如果对比成功让var5+var4(1)

2.1.5 UnSafe底层原理

2.1.5.1 getAndIncrement()

在这里插入图片描述
在这里插入图片描述

2.1.5.2 native修饰的方法代表是底层方法

在这里插入图片描述

2.1.5.3 compxchg

在这里插入图片描述

2.1.5.4 在不通的操作系统下会调用不同的cmpxchg重载函数

在这里插入图片描述
在这里插入图片描述

2.2 总结

CAS是靠硬件实现的从而在硬件层面提升效率,最底层还是交给硬件来保证原子性和可见性实现方式是基于硬件平台的汇编指令,在intel的CPU中(X86机器上),使用的是汇编指令cmpxchg指令。
核心思想是:比较要更新变量的值V和预期值E(compare),相等才会将V的值设为新值N(swap)如果不相等自旋再来。

3 原子引用

3.1 Atomiclnteger 其他原子类型

package com.atguigu.springcloud.util.interrup;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;

import java.util.concurrent.atomic.AtomicReference;
@Getter
@ToString
@AllArgsConstructor
class User{
    String userName;
    int age;
}
public class AtomicReferenceDemo {
    public static void main(String[] args) {
        AtomicReference<User> atomicReference = new AtomicReference<>();
        User z3 = new User("z3",22);
        User li4 = new User("li4",28);

        atomicReference.set(z3);
        System.out.println(atomicReference.compareAndSet(z3,li4)+":"+atomicReference.get().toString());
    }


}

4 CAS与自旋锁

在这里插入图片描述
在这里插入图片描述

4.1 自己实现自旋锁

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;

public class SpinLockDemo {
    AtomicReference<Thread> atomicReference = new AtomicReference<>();
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"come in");
        while (!atomicReference.compareAndSet(null,thread)){}
    }

    public void myUnlock(){
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread,null);
        System.out.println(Thread.currentThread().getName()+" invoked myUnLock()");
    }

    public static void main(String[] args) {
        //自己实现的自旋锁
        /**
         * 实现一个自旋锁
         * 自旋锁好处:循环比较获取没有类似wait的阻塞
         *
         * 通过cas操作来完成自旋锁,A线程先进来调用myLock方法自己持有锁5分钟,
         * B随后进来发现当前所有线程持有锁,所以只能通过自旋等待,直到A释放锁后B随后抢到。
         */
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(()->{
            spinLockDemo.myLock();
            System.out.println(Thread.currentThread().getName()+"正在干活");
            try{
                TimeUnit.SECONDS.sleep(10);}catch (InterruptedException e){e.printStackTrace();}
            spinLockDemo.myUnlock();
        },"AA").start();
        try{TimeUnit.SECONDS.sleep(2);}catch (InterruptedException e){e.printStackTrace();}

        new Thread(()->{
            spinLockDemo.myLock();
            System.out.println(Thread.currentThread().getName()+"正在干活");
            spinLockDemo.myUnlock();
        },"BB").start();
    }
}

5 CAS的缺点

5.1 循环时间长导致cpu开销过大

在这里插入图片描述

5.2 引出ABA问题

在这里插入图片描述
解决:版本号时间戳原子引用

5.2.1 AtomicStampedReference简单case

package com.atguigu.springcloud.util.interrup;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.concurrent.atomic.AtomicStampedReference;

@NoArgsConstructor
@AllArgsConstructor
@Data
class Book{
    private int id;
    private String bookName;
}
public class AtomicStampedDemo {
    public static void main(String[] args) {
        Book javaBook = new Book(1,"javaBook");
        AtomicStampedReference<Book> stampedReference = new AtomicStampedReference<>(javaBook,1);
        System.out.println(stampedReference.getReference()+":"+stampedReference.getStamp());
        Book mysqlBook = new Book(2,"mysqlBook");
        //参数1:期望book 参数2:替换book 参数3:期望版本号  参数4:替换版本号
        boolean b;
        b = stampedReference.compareAndSet(javaBook,mysqlBook,stampedReference.getStamp(),stampedReference.getStamp()+1);
        System.out.println(b+"\t"+stampedReference.getReference()+":"+stampedReference.getStamp());

        //再换回去,增加了版本号的检查
        b = stampedReference.compareAndSet(mysqlBook,javaBook,stampedReference.getStamp(),stampedReference.getStamp()+1);
        System.out.println(b+"\t"+stampedReference.getReference()+":"+stampedReference.getStamp());

    }
}

在这里插入图片描述

5.2.2 AtomicInteger BAB问题复现

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 多线程下ABA问题
 */
public class ABADemo {
    static AtomicInteger atomicInteger = new AtomicInteger(100);
    public static void main(String[] args) {
        new Thread(()->{
            //改为101
            atomicInteger.compareAndSet(100,101);
            try {TimeUnit.MILLISECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
            //再改回100 模拟ABA
            atomicInteger.compareAndSet(101,100);
        },"t1").start();


        //只检查内容忽略了B的操作
        new Thread(()->{
            try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}
            System.out.println(atomicInteger.compareAndSet(100,202)+"\t"+atomicInteger.get());
        },"t2").start();

    }
}

5.2.3 AtomicStampedReference 版本号机制避免ABA

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * 多线程下ABA问题
 */
public class ABADemo {
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);
    public static void main(String[] args) {
        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t"+"首次版本号:"+stamp);
            //保证后面的t4线程拿到的版本号和这里一样
            try {TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}
            atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"\t"+"2次版本号:"+ atomicStampedReference.getStamp());

            atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"\t"+"3次版本号:"+ atomicStampedReference.getStamp());
        },"t3").start();

        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t"+"首次版本号:"+stamp);

            //等待t3发生了ABA问题
            try {TimeUnit.MILLISECONDS.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
            boolean b= atomicStampedReference.compareAndSet(100,2022,stamp,stamp+1);
            System.out.println(b+"\t"+atomicStampedReference.getReference()+"\t"+atomicStampedReference.getStamp());
        },"t4").start();

    }
}

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

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

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

相关文章

Springboot上传文件

上传文件示例代码&#xff1a; ApiOperation("上传文件") PostMapping(value "/uploadFile", consumes MediaType.MULTIPART_FORM_DATA_VALUE) public ApiResult<String> uploadFile(RequestPart("file") MultipartFile file) { //调用七…

从0开始 yolov5可以用灰度图像进行训练和检测吗

yolov5可以用灰度图像进行训练吗,从0开始yolov5灰度图训练和检测 文章目录 yolov5可以用灰度图像进行训练吗,从0开始yolov5灰度图训练和检测[toc]1 预演【表1-1 模型结构截取】 2 修改源码使可以灰度训练2.1 修改读取图片模式2.2 修改源码传参中的通道数2.3 运行train.py2.4 修…

java八股文面试[数据库]——BufferPool

Buffer Pool是MYSQL数据库中的一个重要的内存组件&#xff0c;介于外部系统和存储引擎之间的一个缓存区&#xff0c;针数据库的增删改查这些操作都是针对这个内存数据结构中的缓存数据执行的,在操作数据之前&#xff0c;都会将数据从磁盘加载到Buffer Pool中&#xff0c;操作完…

亚马逊店铺如何快速出单?

对于一个新开的亚马逊店铺而言&#xff0c;首先要做的就是想办法让自己的店铺快速出单&#xff0c;只有有订单了&#xff0c;才能够稳住局势&#xff0c;才能够让自己亚马逊店铺在市场上有立足的资本。 不过对于一个亚马逊店铺而言&#xff0c;要想做到快速出单是很难的&#…

一文读懂GPU显卡的10个重要参数

在当今的高性能计算机世界中&#xff0c;GPU显卡的性能至关重要。这一领域的快速发展&#xff0c;使得图形渲染、游戏体验、视频编辑等高性能计算任务变得更加高效和流畅。正因如此&#xff0c;选择一款合适的GPU显卡变得越来越重要。在挑选GPU显卡时&#xff0c;了解其关键参数…

uni-app:实现右侧弹窗

效果&#xff1a; 代码&#xff1a; <template><view class"container"><button click"showModal true">点击按钮</button><view class"modal-overlay" v-if"showModal" click"closeModal">…

没有软件怎么管理固定资产

在当今数字化的世界中&#xff0c;我们已经习惯了使用各种软件来管理我们的日常生活和工作。然而&#xff0c;当我们面临一个看似简单的问题——如何管理固定资产时&#xff0c;我们可能会感到困惑。那么&#xff0c;如果没有软件&#xff0c;我们该如何进行资产管理呢&#xf…

QT C++ 基于TCP通信的网络聊天室

一、基本原理及流程 1&#xff09;知识回顾&#xff08;C语言中的TCP流程&#xff09; 2&#xff09;QT中的服务器端/客户端的操作流程 二、代码实现 1&#xff09;服务器 .ui .pro 在pro文件中添加network库 .h #ifndef WIDGET_H #define WIDGET_H#include <QWidget>…

Netty—FuturePromise

Netty—Future&Promise 一、JDK原生 Future二、Netty包下的 Future三、Promise1、使用Promise同步获取结果2、使用Promise异步获取结果.3、使用Promise同步获取异常 - sync & get4、使用Promise同步获取异常 - await5、使用Promise异步获取异常 在异步处理时&#xff0…

uniapp - 倒计时组件-优化循环时间倒计时

使用定时器的规避方法 为了避免定时器误差导致倒计时计算错误&#xff0c;可以采用一些规避方法&#xff0c;比如将倒计时被中断时的剩余时间记录下来&#xff0c;重新开启定时器时再将这个剩余时间加到新的计算中。同时&#xff0c;为了避免定时器延迟&#xff0c;可以在每次执…

Golang 新手经常踩的坑

1、 Golang 新手经常踩的坑 1.1 前言 Go 是一门简单有趣的编程语言&#xff0c;与其他语言一样&#xff0c;在使用时不免会遇到很多坑&#xff0c;不过它们大多不是 Go 本身的设计缺陷。如果你刚从其他语言转到 Go&#xff0c;那这篇文章里的坑多半会踩到。 如果花时间学习官…

风向变了!智能汽车何以「降本」

随着软件定义汽车的概念逐步落地&#xff0c;以及底盘、动力、座舱、智驾、车身等不同域&#xff08;分布式或者混合式&#xff09;的功能更新迭代和融合&#xff0c;汽车行业正在意识到&#xff1a;底层硬件架构重构的迫切性。 事实上&#xff0c;早在2016年&#xff0c;作为传…

客户端发现pod并与之通信

客户端发现pod并与之通信 pod需要一种寻找其他pod的方法来使用其他pod提供的服务&#xff0c;不像在没有Kubernetes的世界&#xff0c;系统管理员要在用户端配置文件中明确指出服务的精确IP地址 或者主机名来配置每个客户端应用&#xff0c;但同样的方法在Kubernetes中不适用 …

富硒虫草肉丸系列新品上市—虫草可以“享”着吃

2023年9月4日&#xff0c;鸿祥食品(汕尾市)有限公司探索研发的富硒虫草肉丸全系列新品惊喜亮相&#xff0c;为消费者带来全新的滋补体验。五款以富硒虫草为主要原料&#xff0c;分别以猪肉、鲜虾、牛肉、墨鱼以及牛筋为新食品原料加工而成的虫草类健康新品--“享着丸”系列&…

yolov5运行过程遇到的小问题(随时更新)

1.关于git的问题 解决办法&#xff1a;插入下面代码 import os os.environ["GIT_PYTHON_REFRESH"] "quiet"2.页面太小无法完成操作 解决办法: 如果不好使再考虑降低Batch_Size大小或者调整虚拟内存可用硬盘空间大小&#xff01;&#xff08;调整虚拟内存…

公信力不是儿戏:政府与非营利组织如何利用爱校对提升信息质量

公信力是政府和非营利组织成功的基础&#xff0c;而这种公信力大多来源于对外发布的信息的准确性和可靠性。在这个方面&#xff0c;“爱校对”展现了它的强大能力和实用性。以下我们将具体探讨这两种组织如何通过使用爱校对来提升他们的信息质量。 政府&#xff1a;公开与透明&…

【strapi系列】strapi在登录时调用api/auth/local获取token接口一直报401、403、400错误的问题解决

文章目录 问题描述解决403 forbidden问题解决401 (Unauthorized) error问题调用认证接口需用注意的事项&#xff0c;解决400问题 问题描述 strapi在调用api/auth/local登录接口时&#xff0c;一直报403 forbidden 或 401 (Unauthorized) error问题。 这个接口的作用其实就是使…

02-Linux-IO多路复用之select、poll和epoll详解

前言&#xff1a; 在linux系统中&#xff0c;实际上所有的 I/O 设备都被抽象为了文件这个概念&#xff0c;一切皆文件&#xff0c;磁盘、网络数据、终端&#xff0c;甚至进程间通信工具管道 pipe 等都被当做文件对待。 在了解多路复用 select、poll、epoll 实现之前&#xff…

手写Mybatis:第19章-二级缓存

文章目录 一、目标&#xff1a;二级缓存二、设计&#xff1a;二级缓存三、实现&#xff1a;二级缓存3.1 工程结构3.2 二级缓存类图3.3 二级缓存队列3.3.1 FIFI缓存策略3.3.2 事务缓存3.3.3 事务管理3.3.4 修改一级缓存 3.4 缓存执行器3.4.1 执行器接口3.4.2 执行器抽象基类3.4.…

STM32CUBEMX_创建时间片轮询架构的软件框架

STM32CUBEMX_创建时间片轮询架构的软件框架 说明&#xff1a; 1、这种架构避免在更新STM32CUBEMX配置后把用户代码清除掉 2、利用这种时间片的架构可以使得代码架构清晰易于维护 创建步骤&#xff1a; 1、使用STM32CUBEMX创建基础工程 2、新建用户代码目录 3、构建基础的代码框…