JavaEE——CAS

JavaEE传送门

JavaEE

JavaEE——No.2 多线程案例

JavaEE——常见的锁策略



CAS

CAS: 全称Compare and swap,字面意思: ”比较并交换“. 这是由 CPU 的一条指令, 是原子的.

一个 CAS 涉及到以下操作:

  1. 寄存器 A 的值和内存 M 的值进行比较.
  2. 如果不相同, 则无事发生. 如果相同, 则把寄存器 B 的值和 M 的值进行交换.
  3. 返回操作是否成功

# 注意 #

该操作不关心交换之后 B 的值, 更关心交换之后 M 的值, 此处的交换相当于是把 B 赋值给 M 了.

这是一条由 CPU 的命令, 原子完成的, 是线程安全的 ! ! 效率很高 ! !

CAS 伪代码

boolean CAS(address, expectValue, swapValue) {
 if (&address == expectedValue) {
   &address = swapValue;
        return true;
   }
   return false; 
}

当多个线程同时对某个资源进行 CAS 操作,只能有一个线程操作成功,但是并不会阻塞其他线程,其他线程只会收到操作失败的信号。


CAS 的应用场景

1. 实现原子类

在之前, 使用多线程进行, count++ 操作是线程不安全的, 如果想要安全, 就需要加锁, 而加了锁性能就大打折扣.

这时, 我们就可以基于 CAS 操作来实现 原子 的 count++. 从而保证线程的安全和高效.

伪代码

//标准库中已经封装好的一个类
class AtomicInteger {
    private int value;
    
    //相当于 count++
    public int getAndIncrement() {
        // oldValue 相当于是 寄存器A, 相当于把 内存value 的值读到寄存器里
        int oldValue = value;
        // 此处的 oldValue + 1 相当于是另一个 寄存器B 的值
        while ( CAS(value, oldValue, oldValue+1) != true) {
            //比较 value 这个内存中的值, 是否和 寄存器A 的值相同
            oldValue = value;
       }
        return oldValue;
   }
}

while( ) 解释

  1. 比较 value 这个内存中的值, 是否和 寄存器A 的值相同.
  2. 如果相同, 就把 寄存器B 中的值, 设置到 value 中, 同时 CAS 返回 true, 结束循环.
  3. 如果不相同, 就无事发生. CAS 返回 false, 进入循环, 循环体里面重新读取 内存value 的值到寄存器A 中.

为什么是线程安全的 ?

假设 线程t1 和 线程t2 同时调用 getAndIncrement() 方法. 且执行顺序如下.

如上操作未涉及到挂起等待. 这样的操作, 比等待锁操作, 锁需要的开销低非常多


2. 实现自旋锁

自旋锁, 是纯用户态的轻量级锁, 当发现锁被其他线程持有的时候, 另外的线程不会挂起等待, 而是会反复询问, 看当前的锁是否被释放了

伪代码

public class SpinLock {
    //一个线程示实例, 表示当前这把锁是谁获取到的, null 表示锁的无人获取的状态
    private Thread owner = null;
    public void lock(){
        // 通过 CAS 看当前锁是否被某个线程持有. 
        // 如果这个锁已经被别的线程持有, 那么就自旋等待. 
        // 如果这个锁没有被别的线程持有, 那么就把 owner 设为当前尝试加锁的线程. 
        while(!CAS(this.owner, null, Thread.currentThread())){
       }
   }
    public void unlock (){
        this.owner = null;
   }
}

while( ) 解释

  1. 比较 ownernull 是否相同
  2. 如果相同, 就进行交换, 把当前调用的 lock 的线程的值, 设置到 owner 中(相当于加锁成功), 同时结束循环
  3. 如果不相同, 则 CAS 不进行交换, 返回 false , 再次进入循环, 又会立即再次发起判定

CAS 的 ABA 问题

ABA 问题

CAS 中, 进行比较的时候, 你发现寄存器 A 和内存 M 的值相同, 但你无法判定是 M 始终没变, 还是 M 变了, 又变回来了.

这就好比, 我们买一个手机, 无法判定这个手机是刚出厂的新手机, 还是别人用旧了, 又翻新过的手机.

ABA 问题引来的 BUG

假设 gujiu, 有 1000 存款, 有一天, 她想从 ATM 中取出 500 (ATM 扣款是基于 CAS 来完成的).

恰好在取钱的时候, 卡了以下, gujiu 连按了几下取钱, ATM 创建出了 两个线程, 来进行扣款操作.

那我们如何解决 ABA 问题呢 ?

只需要有一个记录, 能够记录到 内存 中数据的变化, 就可以解决 ABA 问题了.

另外搞一个内存记录版本号, 保存 M 的 “修改次数”[只增不减] , 或者是 “上次修改时间”[只增不减].此时修改操作, 就不是读账户余额了, 而是读内存中的版本号, 且比较的也是的版本号.


🌷(( ◞•̀д•́)◞⚔◟(•̀д•́◟ ))🌷

以上就是今天要讲的内容了,希望对大家有所帮助,如果有问题欢迎评论指出,会积极改正!!
在这里插入图片描述
加粗样式

这里是Gujiu吖!!感谢你看到这里🌬
祝今天的你也
开心满怀,笑容常在。
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
您好!对于JavaEE开发,Eclipse是一个非常流行的集成开发环境(IDE)。它为JavaEE开发者提供了丰富的功能和工具,使他们能够更轻松地构建和管理JavaEE应用程序。 在Eclipse中进行JavaEE开发,您可以使用Eclipse的JavaEE插件(通常称为Eclipse Enterprise Edition for JavaEE),它包含了一系列用于JavaEE开发的工具和模块。这些工具包括Servlet容器、JSP编辑器、EJB开发工具、数据库连接工具等等。 使用Eclipse进行JavaEE开发的步骤如下: 1. 安装Eclipse:您可以从Eclipse官方网站下载适合您操作系统的Eclipse版本,并按照安装指南进行安装。 2. 安装JavaEE插件:在Eclipse中,通过插件安装向导安装JavaEE插件。您可以在Eclipse的市场中搜索并安装适合您版本的JavaEE插件。 3. 创建JavaEE项目:在Eclipse中,可以通过菜单或快捷键创建新的JavaEE项目。选择适当的项目类型(如Dynamic Web Project、EJB Project等),并按照向导进行项目配置。 4. 开发和调试代码:在Eclipse中,您可以使用内置的编辑器编写JavaEE代码,使用调试器进行调试,并利用各种工具和视图来构建和测试您的应用程序。 5. 部署和运行应用程序:完成代码的开发和调试后,您可以将您的JavaEE应用程序部署到适当的服务器上,并启动服务器以运行您的应用程序。 当然,这只是一个简要的介绍。对于JavaEE开发,Eclipse还提供了许多其他功能和工具,如代码自动完成、版本控制、构建工具集成等等。 希望这能帮助到您!如果您还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值