(十一) 共享模型之无锁【CAS 与 volatile】

news2024/9/23 7:29:08

一、问题引出(P158)

1. 取款案例

interface Account {
    // 获取余额
    Integer getBalance();

    // 取款
    void withdraw(Integer amount);

    /**
     * 方法内会启动 1000 个线程,每个线程做 -10 元 的操作
     * 如果初始余额为 10000 那么正确的结果应当是 0
     */
    static void demo(Account account) {
        List<Thread> ts = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            ts.add(new Thread(() -> {
                account.withdraw(10);
            }));
        }
        long start = System.nanoTime();
        ts.forEach(Thread::start);
        ts.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        long end = System.nanoTime();
        System.out.println(account.getBalance()
                + " cost: " + (end-start)/1000_000 + " ms");
    }
}

2. 线程不安全实现

class AccountUnsafe implements Account {

    private Integer balance;

    public AccountUnsafe(Integer balance) {
        this.balance = balance;
    }

    @Override
    public Integer getBalance() {
            return this.balance;        
    }

    @Override
    public void withdraw(Integer amount) {
            this.balance -= amount;        
    }
}

测试:

public class TestAccount {

    public static void main(String[] args) {
        Account account = new AccountUnsafe(10000);
        Account.demo(account);
    }
}

3. 解决思路-

class AccountUnsafe implements Account {

    private Integer balance;

    public AccountUnsafe(Integer balance) {
        this.balance = balance;
    }

    @Override
    public Integer getBalance() {
        synchronized (this) {
            return this.balance;
        }
    }

    @Override
    public void withdraw(Integer amount) {
        synchronized (this) {
            this.balance -= amount;
        }
    }
}

4. 解决思路-无锁

class AccountCas implements Account {
    private AtomicInteger balance;

    public AccountCas(int balance) {
        this.balance = new AtomicInteger(balance);
    }

    @Override
    public Integer getBalance() {
        return balance.get();
    }

    @Override
    public void withdraw(Integer amount) {
        while(true) {
            // 获取余额的最新值
            int prev = balance.get();
            // 要修改的余额
            int next = prev - amount;
            // 真正修改
            if(balance.compareAndSet(prev, next)) {
                break;
            }
        }
    }
}

二、CAS 与 volatile

AtomicInteger 的解决方法,内部并没有用锁来保护共享变量的线程安全。
其中的关键是 compareAndSet ,它的简称就是 CAS (也有 Compare And Swap 的说法),它必须是原子操作。
注意
其实 CAS 的底层是 lock cmpxchg 指令( X86 架构),在单核 CPU 和多核 CPU 下都能够保证【比较 -交换】的原子性。  

1. volatile 

(1)获取共享变量时,为了保证该变量的可见性,需要使用 volatile 修饰。

(2)它可以用来修饰成员变量和静态成员变量,他可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作 volatile 变量都是直接操作主存。即一个线程对 volatile 变量的修改,对另一个线程可见。

注意:

volatile 仅仅保证了共享变量的可见性,让其他线程能够看到最新值,但不能解决指令交错问题(不能保证原子性)。

CAS 必须借助 volatile 才能读取到共享变量的最新值来实现【比较并交换】的效果。

2. 为什么无锁效率高

无锁情况下,即使重试失败,线程始终在高速运行,没有停歇,而 synchronized 会让线程在没有获得锁的时候,发生上下文切换,进入阻塞。打个比喻:

(1)线程就好像高速跑道上的赛车,高速运行时,速度超快,一旦发生上下文切换,就好比赛车要减速、熄火,等被唤醒又得重新打火、启动、加速... 恢复到高速运行,代价比较大。

(2)但无锁情况下,因为线程要保持运行,需要额外 CPU 的支持,CPU 在这里就好比高速跑道,没有额外的跑道,线程想高速运行也无从谈起,虽然不会进入阻塞,但由于没有分到时间片,仍然会进入可运行状态,还是会导致上下文切换。

3.CAS 的特点

结合 CAS 和  volatile 可以实现无锁并发,适用于线程数少、多核 CPU 的场景下。

(1)CAS 是基于乐观锁的思想:最乐观的估计,不怕别的线程来修改共享变量,就算改了也没关系,我吃亏点再重试呗。

(2)synchronized 是基于悲观锁的思想:最悲观的估计,得防着其它线程来修改共享变量,我上了锁你们都别想改,我改完了解开锁,你们才有机会。

(3)CAS 体现的是无锁并发、无阻塞并发:

1️⃣因为没有使用 synchronized,所以线程不会陷入阻塞,这是效率提升的因素之一。

2️⃣但如果竞争激烈,可以想到重试必然频繁发生,反而效率会受影响。

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

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

相关文章

Seata概述基础

分布式事务原因&#xff1a; 单体架构的spring事务不能跨机器&#xff0c;不能跨数据源 分布式事务的概念&#xff1a; 一个业务流程&#xff0c;在分布式系统&#xff08;微服务&#xff09;中&#xff0c;每个业务模块都是一个分支&#xff0c;保证每个业务分支一起成功&am…

ROS1 LTS版本安装教程

Abstract: "ROS is primarily targeted at the Ubuntu , though other Linux systems as well as Mac OS X, Android, and Windows are supported to varying degrees. " 一、系统要求 ROS版本系统版本长期支持ROS Kinetic KameWily: amd64、i386 Xenial: amd64、i…

JSP住宅小区物业管理系统(源代码+开题报告+论文+答辩PPT)科大云炬

小区物业管理毕业设计 &#xff08;论文&#xff09; 目 录 摘要--------------------------------------------------------------------------------------------1 ABSTRACT-----------------------------------------------------------------------------------2 第1章…

制作composer包提供sdk扩展

目录 1、初始化包 2、将代码推送到github远程仓库 3、为写好扩展包打上tag标签标记当前代码版本 4、将包发布到包管理平台 初始化包&#xff0c;生成 Creates a basic composer.json file in current directory composer init composer init 按照引导就可以生成了 , 详细的…

Python学习笔记 - 异常处理

前言 为了增强程序的健壮性&#xff0c;计算机程序的编写也需要考虑如何处理一些异常的情况&#xff0c;Python 语言提供了异常处理功能&#xff0c;本博文主要介绍 Python 异常处理机制。 一 异常处理举例 为了学习 Python 异常处理机制&#xff0c;首先看下面进行除法运算的…

MySQL的主从复制与读写分离详解

MySQL的主从复制与读写分离详解读写分离概述什么是读写分离为什么要读写分离什么时候要读写分离MySQL主从复制与读写分离主从复制的概念MySQL支持的复制类型主从复制的工作过程主从复制示例保证MySQL主从服务器时间同步主节点服务器配置从节点服务器设置验证MySQL读写分离MySQL…

Vue3 学习笔记 —— Hooks、全局函数和变量、Vue3 插件

目录 1. Hooks 1.1 Vue2 中的 mixins 1.1.1 mixins 是什么&#xff1f; 1.1.2 mixins 缺点&#xff1f; 1.2 Vue3 中的 Hooks 1.2.1 Vue3 Hooks 是什么&#xff1f; 1.2.2 Vue3 内置 hooks 举例 1.2.3 自定义 Hooks 2. 全局函数和全局变量 2.1 app.config.globalProp…

【深入设计模式】适配器模式—一切皆有可能

文章目录1. 适配器模式1.1 适配器模式简介1.2 适配器模式结构1.3 适配器模式示例2. 适配器模式在源码中的应用3. 总结适配器这个词来源于硬件领域&#xff0c;是一个独立的硬件设备接口&#xff0c;允许硬件或电子接口与其它硬件或电子接口相连&#xff0c;比如常见的电源适配器…

(附源码)SSM芜湖公共书房服务平台 毕业设计 250859

SSM芜湖公共书房服务平台 摘 要 一座有底蕴的城市&#xff0c;应该是一个阅读的城市&#xff1b;一个有魅力的城市&#xff0c;应该是一个散发着书香的城市&#xff0c;而全民阅读量逐年增加&#xff0c;是社会进步、文明程度提高的重要标志。各大城市启动“全民阅读”工作以来…

Redis缓存过期和和内存淘汰策略

目录 1、MaxMemory 2、Expire数据结构 3、删除策略 3.1、惰性删除 3.2、主动删除 3.3、缓存淘汰策略 ​​​​​​​3.4、缓存淘汰策略的选择 1、MaxMemory Redis作为DB使用时&#xff0c;为了保证数据的完整性&#xff0c;不允许淘汰任何键值对。Redis作为缓存使用时&…

汇编语言与微机原理 期末半开卷复习整理(上)

8086CPU寄存器 8086&#xff1a;16位&#xff0c;4.77MHz~10MHz,16根数据线&#xff0c;20根地址线 AX/AL:乘除法指令中用作累加器&#xff0c;IO指令中用作数据寄存器&#xff0c;可显式或隐含调用 AH&#xff1a;在LAHF用作目的寄存器&#xff0c;隐含调用。 AL&#xff1a;…

大数据必学Java基础(一百一十一):过滤器注解应用和开发案例

文章目录 过滤器注解应用和开发案例 一、过滤器注解应用 二、开发案例

Python学习日记-第三十八天-生成器(第二节)

系列文章目录 使用greenlet&#xff0c;gevent完成多任务一、使用greenlet&#xff0c;gevent完成多任务 这里要先在pycharm里面提前安装好greenlet和gevent的包 操作&#xff1a; 代码&#xff1a; from greenlet import greenlet import timedef test1():while True:prin…

Android入门第39天-系统设置Configuration类

简介 本节给大家介绍的Configuration类是用来描述手机设备的配置信息的&#xff0c;比如屏幕方向&#xff0c; 触摸屏的触摸方式等。 Configuration给我们提供的方法列表 densityDpi&#xff1a;屏幕密度fontScale&#xff1a;当前用户设置的字体的缩放因子hardKeyboardHidd…

[深度学习] python基础支持汇总

这个系列放一些看神经网络源码过程中的python语法现象 文章目录前言一、list操作://extends/append的区别1.引入2.细致场景再现前言 例如&#xff1a;这个系列放一些看神经网络源码过程中的python语法现象, 直接解析语法太干瘪无聊.希望用这个方式来巩固所学知识 一、list操作…

电商评论文本情感分类(中文文本分类+中文词云图)(第一部分-textcnn)

电商评论文本情感分类(中文文本分类中文词云图) 第一部分 第二部分Bert部分 本项目包含&#xff1a; 1.中文文本处理 2.中文词云图绘制 3.中文词嵌入 4.基于textcnn的中文文本分类&#xff08;Test_Acc89.2000&#xff09; 5.基于bert的中文文本分类&#xff08;Test_Acc0.…

ServiceComb场景及其原理

文章目录Java-ChassisEnableServiceComb初始化SCBSPIServiceUtils自定义SPI加载器职责链管理器FilterChainsManager/ConsumerHandlerManagerRpcSchema注册服务如何保活&#xff1f;RpcReferencePropertySourcesPlaceholderConfigurerThreadPoolExecutorEx/LinkedBlockingQueueE…

深刻理解JAVA并发中的有序性问题和解决之道

问题 Java并发情况下总是会遇到各种意向不到的问题&#xff0c;比如下面的代码&#xff1a; int num 0;boolean ready false; // 线程1 执行此方法 public void actor1(I_Result r) {if(ready) {r.r1 num num;} else {r.r1 1;} } // 线程2 执行此方法 public void actor…

Clickhouse 使用DBeaver连接

ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。 据处理大致可以分成两大类&#xff1a;联机事务处理OLTP&#xff08;on-line transaction processing&#xff09;、联机分析处理OLAP&#xff08;On-Line Analytical Processing&#xff09;。 OLTP是传统的…

JavaWeb--JDBC核心技术

JavaWeb--JDBC核心技术JDBC核心技术第1章&#xff1a;JDBC概述1.1 数据的持久化1.2 Java中的数据存储技术1.3 JDBC介绍1.4 JDBC体系结构1.5 JDBC程序编写步骤第2章&#xff1a;获取数据库连接2.1 要素一&#xff1a;Driver接口实现类2.1.1 Driver接口介绍2.1.2 加载与注册JDBC驱…