面试官: 有了解过CAS和原子操作吗?说说看

前言

目前正在出一个Java多线程专题长期系列教程,从入门到进阶含源码解读, 篇幅会较多, 喜欢的话,给个关注❤️ ~

乐观锁 & 悲观锁

在正式讲解之前,我们先了解一下这两个概念

悲观锁

顾名思义,假使情况是最糟糕的,在程序中的体现就是对资源的抢夺。对于悲观锁来说,它总是认为每次访问共享资源时会发生冲突,所以必须对每次数据操作加上锁,以保证临界区的程序同一时间只能有一个线程在执行。

乐观锁

与其相反,乐观锁总是假设对共享资源的访问没有冲突,线程可以不停地执行,无需加锁也无需等待。而一旦多个线程发生冲突,乐观锁通常是使用一种称为CAS的技术来保证线程执行的安全性。

因为没有锁,所以就不存在死锁的情况。大家不仿试想一下,他们分别适合什么样的场景

  • 乐观锁多用于读多写少的环境,避免频繁加锁影响性能

  • 悲观锁多用于写多读少的环境,避免频繁失败和重试影响性能

CAS

概述

CAS的全称是:比较并交换(Compare And Swap)。在CAS中,有这样三个值

  • V:要更新的变量(var)
  • E:预期值(expected) (也称为旧值)
  • N:新值(new)

有没有很熟悉,上节我们讲源码的时候有提到过~ 大致过程如下:

判断更新的变量是否等于预期值,如果等于,将该值设置为新的值;如果不等,说明已经有其它线程更新了变量,则当前线程放弃更新,什么都不做。

那么会不会出现我即将要更新的值被其它线程更新了呢❓

不会的,原因在于CAS是一种原子操作。

原子操作

原子操作(atomic operation)是指不被线程调度打断的操作,通常有一系列操作组合而成,该操作开始到执行结束,期间不会有任何线程切换,从而保证了cpu资源不会被其它线程争夺。简单的来说这种操作不会中断,要么成功,要么失败,可以保证线程的安全性。

「当多个线程同时使用CAS操作一个变量时,只有一个成功更新,其余都失败,但失败的线程并不会被挂起,仅是被告知失败,并且允许再次尝」

Java中的CAS操作具体实现

在Java中,通常使用sun.misc.Unsafe类,该类下的方法都是native方法,所以Java本身不负责具体实现,通常由底层的jvm,c,c++来实现

public boolean compareAndSet(V   expectedReference,
                             V   newReference,
                             int expectedStamp,
                             int newStamp)
 
{
    Pair<V> current = pair;
    return
        expectedReference == current.reference &&
        expectedStamp == current.stamp &&
        ((newReference == current.reference &&
          newStamp == current.stamp) ||
         casPair(current, Pair.of(newReference, newStamp)));
}

循环时间长开销大

CAS多与自旋结合。如果自旋CAS长时间不成功,会占用大量的CPU资源。

我们可以通过JVM支持处理器提供的「pause指令」, 能让自旋失败时cpu睡眠一小段时间再继续自旋,从而使得读操作的频率低很多

只能保证一个共享变量的原子操作

给大家介绍两种方式:

  • 使用提供的AtomicReference

  • 使用锁

结束语

本节主要给大家介绍了CAS和原子操作,有些部分大家了解一下就好。下一节,带大家学习线程池的基本使用 ~

往期内容推荐

项目源码(源码已更新 欢迎star⭐️)

  • java-thread-all

  • 地址: https://github.com/qiuChengleiy/java-thread-all.git

推荐 SpringBoot & SpringCloud (源码已更新 欢迎star⭐️)


本篇文章来源于微信公众号: 程序员皮卡秋



微信扫描下方的二维码阅读本文

此作者没有提供个人介绍
最后更新于 2023-06-27