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

article/2025/9/19 17:50:21

📣 📣 📣 📢📢📢
☀️☀️你好啊!小伙伴,我是小冷。是一个兴趣驱动自学练习两年半的的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 = nullAtomicReference<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;}@Overridepublic 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://chatgpt.dhexx.cn/article/OL1ySC6m.shtml

相关文章

JUC系列(四)

1、CAS 1.1、没有CAS之前&#xff0c;保证线程安全的方式 多线程环境不使用原子类保证线程安全&#xff08;基本数据类型&#xff09; public class T3 {volatile int number 0;//读取public int getNumber(){return number;}//写入加锁保证原子性public synchronized void…

1、什么是juc

1、juc简介 在java中&#xff0c;线程部分是重点&#xff0c;juc就是java.util.concurrent工具包的简称。这是一个处理线程的工具包&#xff0c;从jdk1.5开始出现 2、进程与线程 进程&#xff1a;指在系统中正在运行的一个应用程序&#xff1b;程序一旦运行就是进程&#xff1…

JUC系列(六) 线程池

&#x1f4e3; &#x1f4e3; &#x1f4e3; &#x1f4e2;&#x1f4e2;&#x1f4e2; ☀️☀️你好啊&#xff01;小伙伴&#xff0c;我是小冷。是一个兴趣驱动自学练习两年半的的Java工程师。 &#x1f4d2; 一位十分喜欢将知识分享出来的Java博主⭐️⭐️⭐️&#xff0c;擅…

JUC系列(五)

1、ThreadLocal 1.1、什么是ThreadLocal 线程局部变量。 1.2、ThreadLocal的作用以及可以为什么保证线程安全&#xff1f; 多线程访问同一个共享变量的时候容易出现并发问题&#xff0c;特别是多个线程对一个变量进行写入的时候&#xff0c;为了保证线程安全&#xff0c;一…

JUC系列(二)

1、聊一聊Java“锁” 1.1、乐观锁和悲观锁 悲观锁&#xff1a;认为自己在使用数据的时候一定有别的线程来修改数据&#xff0c;因此在获取数据的时候会先加锁&#xff0c;确保数据不会被别的线程修改。synchronized关键字和Lock的实现类都是悲观锁。适合写操作多的场景&#…

JUC概述

JUC是什么&#xff1f; JUC就是java.util.concurrent,java.util.concurrent.atomic和java.util.concurrent.locks三个工具类包&#xff0c;它们是处理线程的工具包&#xff0c;最开始出现是从JDK 1.5开始出现。&#xff08;JUC就是java.util.concurrent工具类的首字母&#xf…

JUC系列一:什么是JUC

前言&#xff1a;笔记整理参考尚硅谷周阳老师在B站上的JUC教程&#xff0c;万分感谢周阳老师。有兴趣的朋友可以在B站搜索周阳老师的视频教程&#xff0c;绝对让你受益匪浅&#xff0c;期望未来也能成为像周阳老师那样的人O(∩_∩)O哈哈~。 JUC B站视频地址 https://www.bilib…

01_JUC概述

1. JUC是什么&#xff1f; 在 Java 5.0 提供了 java.util.concurrent(简称JUC)包&#xff0c;在此包中增加了在并发编程中很常用的工具类。此包包括了几个小的、已标准化的可扩展框架&#xff0c;并提供一些功能实用的类&#xff0c;没有这些类&#xff0c;一些功能会很难实现或…

JUC系列(三)

1、Java内存模型 1.1、什么是Java内存模型JMM&#xff1f; JMM(Java内存模型Java Memory Model&#xff0c;简称JMM)本身是一种抽象的概念并不真实存在&#xff0c;它仅仅描述的是一组约定或规范&#xff0c;通过这组规范定义了程序中(尤其是多线程)各个变量&#xff08;包括…

JUC系列——基础知识 day1-1

JUC系列——基础知识 day1-1 JUC基础知识进程线程进程和线程区别并行与并发同步使用场景 异步使用情景 QuickStart&#xff08;new Thread方式创建新线程&#xff09;匿名内部类方式lambda简化 Thread类&#xff08;仅分析功能&#xff09;优先级常量方法&#xff08;常用&…

JUC系列(六)

1、AbstractQueuedSynchronizer之AQS AbstractQueuedSynchronizer简称为AQS&#xff0c;抽象的队列同步器 AQS&#xff1a;是用来构建锁或者其它同步器组件的重量级基础框架及整个JUC体系的基石&#xff0c;使用一个int类变量表示持有锁的状态&#xff0c;通过内置的FIFO队列…

JUC系列(一)

1、为什么多线程极其重要&#xff1f; ​ 多线程变得极其重要的原因从软硬件两个方面来说&#xff0c;首先硬件方面&#xff1a;主要是摩尔定律失效&#xff0c;它是由英特尔创始人之一Gordon Moore(戈登摩尔)提出来的,其内容是当价格不变时&#xff0c;将每隔18个月&#xff…

JUC系列(一)什么是JUC?

多线程一直Java开发中的难点&#xff0c;也是面试中的常客&#xff0c;趁着还有时间&#xff0c;打算巩固一下JUC方面知识&#xff0c;我想机会随处可见&#xff0c;但始终都是留给有准备的人的&#xff0c;希望我们都能加油&#xff01;&#xff01;&#xff01; 沉下去&#…

JUC系列——JUC入门及多线程并发

文章目录 前言一、JUC简介1、出自哪位大神之手&#xff1f;2、JUC是什么&#xff1f; 二、JUC主要解决什么问题&#xff1f;1、进程 、线程2、并发、并行3、JAVA创建线程的方式&#xff08;模拟三种线程实现方式&#xff09;1&#xff09;继承Thread类&#xff08;线程的初级用…

Java进阶|JUC系列(持续更新)

文章目录 1、什么是J.U.C2、进程、线程、协程2.1 简介2.2 线程有几个状态2.3 wait和sleep的区别 3、Lock锁(重点)3.1 简介3.2 lock锁和synchronized有什么区别 4、生产者和消费者问题4.1 简介4.2 使用synchronized实现的demo4.3使用Lock实现的demo 5、锁的是谁问题1、两个线程访…

莱布尼茨公式C语言编程,高等数学——手撕牛顿莱布尼茨公式

本文始发于个人公众号&#xff1a;TechFlow&#xff0c;原创不易&#xff0c;求个关注 今天是高等数学专题的第13篇文章&#xff0c;我们来看看定积分究竟应该怎么计算。 定积分的实际意义 通过之前的文章&#xff0c;我们基本上熟悉了定积分这个概念和它的一些简单性质&#x…

[计算机数值分析]牛顿插值公式

Spring-_-Bear 的 CSDN 博客导航 埃特金算法虽然具有承袭性&#xff0c;但其算式是递推型的&#xff0c;不便于进行理论上的分析。所以采用具有承袭性的显式的牛顿插值公式是不错的选择。 p n ( x ) f ( x 0 ) f ( x 0 , x 1 ) ( x − x 0 ) . . . f ( x 0 , x 1 , . . . …

如何用计算机做牛顿迭代公式,牛顿迭代法

牛顿迭代法(Newtons method)又称为牛顿-拉夫逊(拉弗森)方法(Newton-Raphson method),它是牛顿在17世纪提出的一种在实数域和复数域上近似求解方程的方法。 中文名 牛顿迭代法 外文名 Newtons method别 名 牛顿-拉夫逊(拉弗森)方法 提出时间 17世纪 牛顿迭代法产生背景 编辑…

牛顿恒等式 牛顿和

前言&#xff1a;仅个人小记。该恒等式推导逻辑非常简洁。目标&#xff1a;求一个多项式的所有根的次幂和。比如多项式 P ( x ) Σ i 0 n a i x i P(x)\Sigma_{i0}^{n}a_i x^i P(x)Σi0n​ai​xi的根为 α , β , . . . , ω \alpha,\beta,...,\omega α,β,...,ω&#xff…