JUC系列(九) CAS 与锁的理解

news2024/10/1 5:24:51

📣 📣 📣 📢📢📢
☀️☀️你好啊!小伙伴,我是小冷。是一个兴趣驱动自学练习两年半的的Java工程师。
📒 一位十分喜欢将知识分享出来的Java博主⭐️⭐️⭐️,擅长使用Java技术开发web项目和工具
📒 文章内容丰富:覆盖大部分java必学技术栈,前端,计算机基础,容器等方面的文章
📒 如果你也对Java感兴趣,关注小冷吧,一起探索Java技术的生态与进步,一起讨论Java技术的使用与学习
✏️高质量技术专栏专栏链接: 微服务数据结构netty,单点登录,SSMSpringCloudAlibaba
😝公众号😝想全栈的小冷,分享一些技术上的文章,以及解决问题的经验
当前专栏JUC系列

深入理解CAS

什么是 CAS

CAS compareAndSet 比较并交换

研究底层,才会有所突破

实例代码

    //CAS compareAndSet 比较并交换
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        // 两个参数 : 期望 更新
        //   public final boolean compareAndSet(int expectedValue, int newValue)
        // 如果我们的期望值达到了 那么就更新,否则 就不更新 CAS 是 CPU 并发原语
        atomicInteger.compareAndSet(2020, 2021);
        System.out.println(atomicInteger.get());
        atomicInteger.getAndIncrement();
        atomicInteger.compareAndSet(2020, 2021);
        System.out.println(atomicInteger.get());
    }

加一 方法底层原理 为什么效率高

  1. 调用了 unsafe 操作内存的方法
  2. 查看这个getAndAddInt这个方法的参数, var 1 就是原本数字 var 2 就是valueoffset ,var 4 就是要增加多少
  3. var 是获取内存值,之后调用方法 如果 var1 和 var2 的结果是我们想要的 也就是 var5 那么就讲 var5+var4 也就是原本的结果 +1

image-20220304173908384

这个方法是一个典型的自旋锁

CAS:比较当前工作内存中的值,如果这个值是期望的,那么执行操作,如果不是就一直循环

缺点:

  1. 循环会耗时
  2. 一次性只能保证一个共享变量
  3. ABA问题

unsafe类

image-20220304173602223

CAS ABA问题

A:期望是 1 交换成2 ,但是在还没有交换的时候 另一个线程 把 当前的a 改变成了 3 又改回 1 此时 当前A线程依旧可以正常的交换,但是期间的值已经被别人用过了。

image-20220304224113617

    //CAS compareAndSet 比较并交换
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        //对于 我们平时都 sql 是如何解决的,乐观锁
        // 两个参数 : 期望 更新
        //   public final boolean compareAndSet(int expectedValue, int newValue)
        // 如果我们的期望值达到了 那么就更新,否则 就不更新 CAS 是 CPU 并发原语
        //=============捣乱的线程====================
        atomicInteger.compareAndSet(2020, 2021);
        System.out.println(atomicInteger.get());
        atomicInteger.compareAndSet(2021, 2020);
        System.out.println(atomicInteger.get());

        //=================期望的线程=============
        atomicInteger.compareAndSet(2020, 2021);
        System.out.println(atomicInteger.get());
    }

原子引用(解决aba问题)

解决ada问题

原子引用 AtomicStampedReference

可以理解为 乐观锁

PS: integer 使用了 对象缓存机制,默认范围是 -128-127 ,推荐使用静态工厂方法 valueof 获取对象实例,而不是new ,因为 value of 使用缓存,而new 一定会创建心的对象分配新的内存空间

image-20220305021114344

代码实例

  //CAS compareAndSet 比较并交换
    public static void main(String[] args) {
        //AtomicInteger atomicStampedReference = new AtomicInteger(2020);
        // 注意 如果 泛型是一个包装类,注意对象的引用问题,
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);
        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println("a1 =>" + stamp);
            System.out.println("a2 =>" + atomicStampedReference.compareAndSet(1, 2, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("a2 =>" + atomicStampedReference.getStamp());
            System.out.println("a3 =>" + atomicStampedReference.compareAndSet(2, 1, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("a3 =>" + atomicStampedReference.getStamp());
        }, "a").start();
        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println("b1 =>" + stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("b2 =>" + atomicStampedReference.compareAndSet(1, 6, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("b2 =>" + atomicStampedReference.getStamp());
        }, "b").start();
    }

对于锁的理解

1、公平锁和非公平锁的区别

公平锁 :不能够插队,必须先来后到

非公平锁: 可以插队 锁 默认的都是非公平的

    public ReentrantLock() {
        sync = new NonfairSync();
    }

也可以修改成 公平锁

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

2、可重入锁

image-20220305021641302

sync关键字

这里 是一把锁,每次执行的时候直到方法里逐层向外解锁

public class lockdemo {
    public static void main(String[] args) {
        phone phone = new phone();
        new Thread(() -> {
            phone.sms();
        }, "a").start();
        new Thread(() -> {
            phone.sms();
        }, "b").start();
    }
}

class phone {
    public synchronized void sms() {
        System.out.println(Thread.currentThread().getName() + "=>发短信");
        //这里也有锁
        call();
    }

    public synchronized void call() {
        System.out.println(Thread.currentThread().getName() + "=>打电话");
    }
}

Lock

  1. lock 锁每个方法是配对一个锁,像下面的例子就是开了两个锁,锁必须配对
public class lockdemo2 {
    public static void main(String[] args) {
        phone2 phone = new phone2();
        new Thread(() -> {
            phone.sms();
        }, "a").start();
        new Thread(() -> {
            phone.sms();
        }, "b").start();
    }
}

class phone2 {
    //这里就有区别 sync关键字是一个锁,这里使用lock 是两个锁,锁必须配对,否则就会死锁
    Lock lock = new ReentrantLock();

    public synchronized void sms() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "=>发短信");
            //这里也有锁
            call();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public synchronized void call() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "=>打电话");
            //这里也有锁
            call();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

3、自旋锁

这里我们之前查看CAS 的时候 有一个调用自增的方法就是自旋锁

image-20220305022601565

自己的简易自旋锁

public class spinlocks {
    //    int =0
//    thread = null
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    //    加锁操作
    public void mylock() {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName() + "=> mylock");
        while (!atomicReference.compareAndSet(null, thread)) {
        }
    }

    //    解锁
    public void myunlock() {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName() + "=> myUnlock");
        atomicReference.compareAndSet(thread, null);
    }
}

class test {
    public static void main(String[] args) throws InterruptedException {
        //底层使用 CAS 自旋锁
        spinlocks lock = new spinlocks();
        new Thread(() -> {
            lock.mylock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myunlock();
            }
        }, "t1").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
            lock.mylock();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myunlock();
            }
        }, "t2").start();
    }
}

4、死锁

什么是死锁: 互相争抢锁的过程

image-20220305023700803

死锁测试,如何排查死锁

public class DeadLockDemo {
    public static void main(String[] args) {
        String a = "lockA";
        String b = "lockB";
        new Thread(new mythread(a, b), "t1").start();
        new Thread(new mythread(b, a), "t2").start();
    }
}

class mythread implements Runnable {
    private String a;
    private String b;

    public mythread(String a, String b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public void run() {
        synchronized (a) {
            System.out.println(Thread.currentThread().getName() + "lock=>" + a + "lock=>" + b);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (b) {
                System.out.println(Thread.currentThread().getName() + "lock=>" + b + "lock=>" + a);
            }
        }
    }
}

解决问题

1、使用 jps -l 定位 进程

image-20220305025155109

2、 使用jstack+进程号 2916

image-20220305025252482

面试或者工作中,排查锁的问题:

  1. 日志 百分之九十
  2. 堆栈 百分之十

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

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

相关文章

DZ-51/220【中间继电器】

系列型号 DZ-51中间继电器; DZ-52中间继电器; DZ-53中间继电器; DZ-54中间继电器; JY-16A中间继电器; 一、用途 DZ-51/220;DZ-51/400中间继电器​ (以下简称继电器)用于自动控制装置中&#xff0c;以扩大被控制的电路和提高接通能力。 二、主要技术参数 额定电压&#xff1a;D…

Spring(存储Bean对象五大类注解,获取Bean对象三种注入方式)

目录 1. 先进行配置扫描路径 2. 存储 Bean 对象 ( 添加注解存储 Bean 对象) 2.1 Controller [控制器存储] 2.2 Service [服务存储] 2.3 Repository [仓库存储] 2.4 Configuration [配置存储] 2.5 Component [组件存储] 2.6 五大类注解的作用和关系 2.7 Bean的命名规则…

基于文化算法优化的神经网络预测研究(Matlab代码实现)

目录 1 文化优化算法 2 人工神经网络 3 基于文化算法优化的神经网络预测研究&#xff08;Matlab代码实现&#xff09;运行结果 4 参考文献 5 Matlab代码实现 1 文化优化算法 大自然里的各种生物在生存环境中相互竞争&#xff0c;优胜劣汰&#xff0c;不断进化&#xff0…

股票编程交易接口怎样自动设置止损?

目前随着国内二级股市已处于相对较高的水平&#xff0c;但是对于高净值的用户来说&#xff0c;在量化投资市场上使用股票编程交易接口系统上具有低波动性和自动交易的定量对冲投资模型体系&#xff0c;包括了一些高性价比、大型基金和长期投资的资产品种&#xff0c;所以大家在…

瑞吉外卖-B站最火的新手入门SpringBoot+Mybatis+Redis项目详细教程来了

&#x1f531;项目介绍 项目地址 GitHub&#xff1a;https://github.com/codermast/Takeout-food 对你有用的话&#xff0c;希望能给项目点个Star&#xff0c;非常感谢。 对于项目的任何问题&#xff0c;或者你在本地部署时遇到的无法解决的问题&#xff0c;都可以提交issues…

迭代器模式

文章目录思考迭代器模式1.迭代器模式的本质2.何时选用迭代器模式3.优缺点4.实现手动实现迭代器模式java迭代器模式思考迭代器模式 迭代器模式(Iterator Pattern)是常用的设计模式&#xff0c;属于行为型模式。如果我们的集合元素是用不同的方式实现的&#xff0c;有数组&#…

利用EasyDL制作一个简单的图片识别小项目

主要是利用EasyDL制作一个简单的傻瓜式猫狗图片识别&#xff0c;利用EasyDL&#xff0c;只需要几步简单的点击即可 *主要的步骤&#xff1a; 1.准备数据 2.训练模型 3.部署 4.H5 * 1.首先创建两个文件夹cat和dog分别搜集至少20张以上的猫和狗的图片&#xff08;如果不足20张&…

【High 翻天】Higer-order Networks with Battiston Federico (1)

目录前言高阶网络的表示高阶相互作用的初等表示表示之间的关系总结前言 Ginestra Bianconi 的一篇综述1和一本书2展开的故事。 书还没有开始看&#xff0c;先看看综述吧。综述提供了一个关于超越成对交互的网络新兴领域的概述。 讨论了高阶网络的表示&#xff0c;主要是其中…

HBase Shell 基本操作

首先确保你的hbase集群已经开启 进入 HBase 客户端命令行 namespace 使用help查看命令用法&#xff0c;并创建命名空间 查看所有命名空间 DDL 在 bigdata 命名空间中创建表格 student&#xff0c;两个列族。info 列族数据维护的版本数为 5 个&#xff0c; 如果 VERSIONS …

Arduino UNO通过SPI串行方式驱动LCD12864液晶屏

LCD12864液晶屏简介 LCD12864带中文字库图形点阵式液晶显示器&#xff0c;可配合各种单片机可完成中文汉字、英文字符和图形显示&#xff0c;可构成全中文人机交互图形界面&#xff0c;模块具有功耗低、显示内容丰富等特点而应用广泛。它是利用液晶经过处理后能改变光线的传播…

前端面试八股文(详细版)—上

本文将详细讲解 HTML 、CSS 、JavaScript 、计算机网络知识等方面的内容&#xff0c;Vue 、React 、git 、项目开发实例等内容放在下篇&#xff0c;预祝各位成功上岸&#xff01; 第一板块&#xff1a;JavaScript ES6 新增内容 1、模板字符串 变量名写在 ${} 中&#xff0c;$…

可用性测试的理解

可用性测试是通过观察有代表性的用户&#xff0c;完成产品的典型任务&#xff0c;从而界定出可用性问题并解决的过程。通俗地讲就是“观察用户使用产品”。 可用性测试到底测的什么? a 有效性(独立完成任务的比例) b 完成任务的时间 c 满意度(用户主观评价操作难易/好感/是否再…

回归预测 | MATLAB实现贝叶斯优化门控循环单元(BO-GRU)多输入单输出

回归预测 | MATLAB实现贝叶斯优化门控循环单元(BO-GRU)多输入单输出 目录回归预测 | MATLAB实现贝叶斯优化门控循环单元(BO-GRU)多输入单输出基本介绍背景回顾模型介绍程序设计学习总结参考资料致谢基本介绍 本次运行测试环境MATLAB2020b&#xff1b; 本次预测基本任务是回归&a…

【JavaEE】JavaScript(基础语法)2

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录前言四、【运算符】五、条件、三元、switch、循环语句六、数组七、函数八、 对象THINK前言 一个人最大的痛苦来自于对自己无能的愤怒&#xff01; Hi&#xff0c;这里依旧是秃头宝贝儿&#xff01; 本文依旧是介绍…

win10录屏功能怎么打开,详细图文教学,轻松学会

在生活中&#xff0c;有不少人的电脑&#xff0c;都是WIN10系统&#xff0c;在学习工作或者娱乐的过程中想要录下视频&#xff0c;就需要使用win10录屏功能。Win10录屏功能怎么打开&#xff1f;Windows自带的屏幕录制功能如何使用&#xff1f;不要着急&#xff0c;今天小编将向…

尚医通-微信支付

流程梳理 依赖和工具类 <dependency><groupId>com.github.wxpay</groupId><artifactId>wxpay-sdk</artifactId><version>0.0.3</version> </dependency><dependency><groupId>com.example</groupId><ar…

Collagen胶原蛋白修饰亚油酸linoleic acid/甲磺酸酯Mesylate/磷酸三苯酯TPP

产品名称&#xff1a;胶原蛋白修饰亚油酸 英文名称&#xff1a;Collagen-linoleic acid 用途&#xff1a;科研 状态&#xff1a;固体/粉末/溶液 产品规格&#xff1a;1g/5g/10g 保存&#xff1a;冷藏 储藏条件&#xff1a;-20℃ 储存时间&#xff1a;1年 胶原蛋白是一种细胞外蛋…

Mybatis:Mybatis中特殊Sql执行(6)

特殊Sql执行1. 模糊查询2. 批量删除3. 动态设置表名4. 添加功能获取自增的主键5. 整体代码1. 模糊查询 /*** 根据用户名进行模糊查询* param moHu* return*/List<User> getUserByLike(Param("moHu") String moHu);<!--List<User> getUserByLike(Param…

Java多线程之相关拓展(静态代理和Lamda表达式演变)

Java多线程拓展之静态代理和Lamda表达式演变一、静态代理1、概念2、优点3、样例二、Lamda表达式&#xff08;演变过程展示&#xff09;1、定义一个函数式接口并实现类&#xff08;最初状态&#xff09;2、静态内部类&#xff08;状态演变2&#xff09;3、局部内部类&#xff08…

英国G5生物医学类专业IB成绩要求多高?

生物医学类专业一向是申请热门。最近几年&#xff0c;由于新冠疫情的原因&#xff0c;相关专业申请竞争更加激烈了。英国G5生物医学类专业IB成绩要求多高&#xff1f;话说&#xff0c;IB申请英国的生物医学类专业&#xff0c;需要做好哪些准备&#xff1f;英国大学的生物医学类…