【Java开发】JUC进阶 06:异步回调、JMM、Volatile

news2025/1/21 10:07:46

1 异步回调

异步是多线程的一种特殊实现方式

📌 举例

  1. 我需要一个计算时间5秒方法的返回值

  1. 我不想等这5秒钟,我想要继续执行下面的代码,那就异步执行这个方法

  1. 当我通过get去获取这个返回值时,如果已经过了5秒,也就是方法执行完了,那我就可以直接得到返回值

  1. 如果方法还没执行完,这个时候就需要等待执行完才能拿到返回值

📌 没有返回值的 runAsync 异步回调

    CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"runAsync=>Void");
    });
    
    System.out.println("---------------");
    completableFuture.get();//获取阻塞执行结果

📌 有返回值的 runAsync 异步回调

    CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
        System.out.println(Thread.currentThread().getName()+"supplyAsync=>Integer");
        return 1024;
    });

    completableFuture.whenComplete((t,u)->{
        System.out.println("t=>"+t);//正常的返回结果
        System.out.println("u=>"+u);//错误的信息
    }).exceptionally((e)->{
        System.out.println(e.getMessage());//可以获取到错误的结果
        return 400;
    });

2 JMM

JMM:java内存模型,是一种约定。

  • 线程解锁前,必须把共享变量立刻刷回主存

  • 线程加锁前,必须读取主存中的最新值到工作内存中

  • 加锁和解锁是同一把锁

📌 JMM模型

📌 JMM八大操作

为了支持 JMM,Java 定义了8种原子操作,用来控制主存与工作内存之间的交互:

  1. read 读取:作用于主内存,将共享变量从主内存传送到线程的工作内存中。

  1. load 载入:作用于工作内存,把 read 读取的值放到工作内存中的副本变量中。

  1. store 存储:作用于工作内存,把工作内存中的变量传送到主内存中。

  1. write 写入:作用于主内存,把从工作内存中 store 传送过来的值写到主内存的变量中。

  1. use 使用:作用于工作内存,把工作内存的值传递给执行引擎,当虚拟机遇到一个需要使用这个变量的指令时,就会执行这个动作。

  1. assign 赋值:作用于工作内存,把执行引擎获取到的值赋值给工作内存中的变量,当虚拟机栈遇到给变量赋值的指令时,就执行此操作。

  1. lock锁定: 作用于主内存,把变量标记为线程独占状态。

  1. unlock解锁: 作用于主内存,它将释放独占状态。

3 Volatile

Volatile是java虚拟机提供的轻量级的同步机制

  1. 保证可见性

  1. 不保证原子性

  1. 禁止指令重排

3.1 Volatile可见性解决ABA问题

📌 ABA问题场景

public class JMMDemo {
    private static int num = 0;
    public static void main(String[] args) {

        new Thread(()->{
            while (num==0){

            }
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        num = 1;
        System.out.println(num);
    }
}

控制台输出:

📢 程序没有停止,A线程还在循环中,因为线程A没察觉到num改变了,这就是ABA问题。

📌 解决

  • 不加 volatile 程序就会死循环

  • 加 volatile 可以保证可见性

往变量前缀加入volatile即可~

控制台输出:

3.2 不保证原子性

原子性:不可分割,线程A在执行任务的时候,不能被打扰,也不能被分割。要么同时成功,要么同时失败。

📌 代码举例

//volatile 不保证原子性
public class VDemo1 {

    private volatile static int num = 0;

    public static void add(){
        num++;
    }

    public static void main(String[] args) {
        //理论上num结果为20000
        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int i1 = 0; i1 < 1000; i1++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){ //main线程礼让,这样就可以最后再执行
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+" "+num);
    }
}

控制台输出:

其实num++;不是原子性操作,他有以下步骤:

  1. 获得该值

  1. +1

  1. 写回这个值

📌 使用原子类解决

这些类的底层都和直接和操作系统挂钩!直接在内存中修改值。

//volatile 不保证原子性
public class VDemo1 {

    // 原子类的int
    private volatile static AtomicInteger num = new AtomicInteger();

    public static void add(){
        //AtomicInteger +1 方法,CAS--底层
        num.getAndIncrement();
    }

    public static void main(String[] args) {
        //理论上num结果为20000
        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int i1 = 0; i1 < 1000; i1++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){ //main线程礼让,这样就可以最后再执行
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+" "+num);
    }
}

3.3 指令重排

📌 要点

  • JVM自动优化编写的代码,不是自上而下的运行,会对代码重新排序后执行。

  • 源代码-->编译器优化的重排-->指令并行可能重排-->内存系统可能重排-->执行

int x = 1; //1
int y = 1; //2
x = x + 5; //3
y = x + x; //4

源代码顺序是:1234 实际执行可能是2134、1324

📌 Volatile避免指令重排

设置内存屏障,是CPU指令~

  1. 保证特定操作的执行顺序

  1. 可以保证某些变量的内存可见性

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

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

相关文章

16、参数估计

概率基本定义先验分布&#xff1a;似然函数&#xff1a;后验分布&#xff1a;贝叶斯公式&#xff1a;&#xff0c;其中后验分布 似然函数 先验分布 / P(D)贝叶斯公式假设&#xff0c;现在有两个一定概率发生的事件A和B&#xff0c;且它们之间存在一定的关系P(A) 表示事件A发生…

UNIX网络编程卷一 学习笔记 第八章 基本UDP套接字编程

UDP是无连接不可靠的数据报协议&#xff0c;不同于TCP提供的面向连接的可靠字节流。使用UDP编写的常见程序有&#xff1a;DNS、NFS、SNMP。 以下是典型的UDP客户/服务器程序的函数调用&#xff0c;客户不与服务器建立连接&#xff0c;而是只使用sendto函数给服务器发送数据报&…

03_Linux压缩解压,用户用户组,文件权限

目录 Linux下常用的压缩格式 gzip 压缩工具 gzip 对文件夹进行压缩 bzip2 压缩工具 tar打包工具 对.tar.bz2 进行压缩和解压缩 对.tar.gz 进行压缩和解压缩 rar格式 zip格式 Linux用户 Linux用户组 创建用户和用户组 Linux文件权限 Linux文件权限修改 Linux下常用…

windows10安装nodejs

一、下载 官网 Download | Node.js 某云盘 链接&#xff1a;https://pan.baidu.com/s/1PCd4fh4ohEvAc8qSrb4-WA 提取码&#xff1a;yola 二、安裝 双击安装程序&#xff1a;具体步骤如下&#xff1a; 设置环境变量 验证安装是否安装成功 进入cmd命令行窗口&#xff0c;输入…

SQL优化操作1

一.存储过程建表 建两张30万条数据的表以做测试用 1.user_course_info delimiter $$ # 定义结束符 drop procedure if exists addTestDataOne; # 存储过程名叫&#xff1a;addTestData create procedure addTestDataOne() begin declare number int; set number 1; w…

node 配置 vue npm配置

下载node 版本16https://nodejs.org/download/release/v16.16.0/node-v16.16.0-x64.msi复制安装地址&#xff0c;省空间&#xff0c;生报错老老实实复制就好D:\Program\nodejs新建node_cache和node_globalD:\Program\nodejs\node_cacheD:\Program\nodejs\node_global运行命令np…

Sentinel架构篇 - 熔断降级

熔断降级 概念 除了流量控制以外&#xff0c;对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用其它模块&#xff0c;可能是一个远程服务、数据库、或者第三方 API 等。然而&#xff0c;被依赖的服务的稳定性是不能保证的。如果依赖的服…

原生HTML放大镜

该放大区域用背景图片放大 <!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-UA-Compat…

Linux - buff和cache的区别

free -h命令可以查看内存的使用情况 [rootzabbix-server ~]# free -htotal used free shared buff/cache available Mem: 1.8G 432M 894M 10M 492M 1.2G Swap: 2.0G 0B 2.0G为什…

抖音小程序实践四:实现小程序分享

有时候我们要把一个小程序分享给别人&#xff0c;去看套餐、买东西之类的&#xff0c;是一个很常见的功能&#xff0c;但是在接入抖音小程序的时候&#xff0c;初始化右上角三个点并没有分享的入口&#xff0c;那看来不是要申请&#xff0c;就是有别的开发的口子了。下面我们一…

《SQL基础》17. InnoDB引擎

InnoDB引擎InnoDB引擎逻辑存储结构架构内存结构磁盘结构后台线程事务原理事务基础redo logundo logMVCC基本概念隐式字段undo log版本链readView原理分析InnoDB引擎 逻辑存储结构 InnoDB的逻辑存储结构如下图所示&#xff1a; 表空间 表空间是InnoDB存储引擎逻辑结构的最高层…

React18 setState是同步还是异步?

相信大家对于react的setState肯定是不陌生了, 这是一个用于更新状态的函数. 但是在之前有一道非常经典的面试题就是关于setState是同步还是异步的问题, 具体可以参考我之前写的一篇文章: 一篇文章彻底理解setState是同步还是异步&#xff01;. 对于react 18之前的版本, 上文说的…

2019年MathorCup数学建模A题数据驱动的城市轨道交通网络优化策略解题全过程文档及程序

2019年第九届MathorCup高校数学建模挑战赛 A题 数据驱动的城市轨道交通网络优化策略 原题再现&#xff1a; 截至 2018 年 12 月 31 日&#xff0c;中国内地累计共有 35 座城市建成并投运城市轨道交通&#xff0c;里程共计 5766.6 公里。进入“十三五”以来&#xff0c;三年累…

Spring Bean实例创建装载过程分析-spring源码学习(2)

随着Spring框架的应用越来越广泛&#xff0c;对Spring Bean的实例创建装载过程的了解就显得尤为重要。本文将围绕这一主题&#xff0c;为大家详细介绍Spring Bean实例创建装载的整个过程&#xff0c;并透彻解析其细节。 时序图 一、Spring Bean实例的创建过程 Spring Bean实例…

Web前端学习:章三 -- JavaScript预热(二)

六五&#xff1a;作用域与function function&#xff1a;函数&#xff0c;不是数学上的函数&#xff0c;与写代码有关 JS中的函数&#xff1a;运用它&#xff0c;起个名字&#xff0c;然后对函数进行调用&#xff0c;即可将函数中的内容执行一遍 1、function 最基本的作用域…

CNCF x Alibaba云原生技术公开课 第五章 应用编排与管理

1、元数据的组成 用来识别资源的具有标识型的标签&#xff1a;Labels key valueselector(筛选/组合资源):多个相等条件&#xff0c;逻辑与的关系; 集合型,in notin 用来描述资源的非标识型的注解&#xff1a;Annotations 扩展资源的spec/status可以包含特殊字符可以结构化也可…

企业管理经典书籍推荐

几乎每一位成功的商业人士都有着良好的阅读习惯。并且他们阅读涉猎的范围也大多与企业管理和领导力有关。而关于企业管理经典书籍&#xff0c;我推荐你看以下这两本。一本是《经理人参阅&#xff1a;企业管理实务》&#xff0c;另一本是《经理人参阅&#xff1a;领导力提升》。…

无刷高速风筒方案介绍--【PCBA方案】

疫情三年过去&#xff0c;春节后&#xff0c;一个新的开始&#xff0c;大家满怀希望畅谈今年好气象。 三年来一波一波的封城、隔离、核酸&#xff0c;经济压抑到了无以复加的地步&#xff0c;也导致了诸多社会问题的出现。消费力被磨平&#xff0c;人们小心翼翼的生活。 常跟…

【第六课】Arcgis中基本操作

一、前言 前面课程已经对Arcgis主页面&#xff0c;相关板块进行介绍&#xff0c;相信大家也有了一定的了解&#xff0c;当然这部分内容其实不需要大家死记硬背&#xff0c;有一个初步印象即可&#xff0c;这一节课程可能更需要掌握&#xff0c;之后会慢慢有实例给大家展现&…

数据结构刷题(二十):17电话号码的字母组合、39组合总和、40组合总和II

一、电话号码的字母组合题目链接思路&#xff1a;回溯三部曲。确定回溯函数参数&#xff1a;题目中给的 digits&#xff0c;还要有一个参数就是int型的index&#xff08;记录遍历第几个数字&#xff0c;就是用来遍历digits的&#xff0c;同时也代表了递归的深度&#xff09;&am…