Java并发编程基础-Java内存模型、volatile关键字与指令重排序及happens-before原则

JAVA并发编程实践.pdf 带目录书签 清晰版 立即下载

Java并发编程实战学习笔记

 

目录

1.什么是Java内存模型?它和JVM内存模型有什么区别?

2.Java内存模型有哪些组成部分?

3.volatile关键字的作用?

4.什么是指令重排序

5.指令重排序必须满足什么条件

6.指令重排序会对多线程造成影响吗

7.什么是happens-before关系

8.happens-before和as-if-serial语义之间的区别

9.happens-before规则

8.双重检查加锁下的重排序问题


 

1.什么是Java内存模型?它和JVM内存模型有什么区别?

Java内存模型的作用是控制Java线程之间的通信,它决定了一个线程对共享变量的写入什么时候对另一个线程可见;而JVM内存模型指的是Java虚拟机运行时的内存分区。

2.Java内存模型有哪些组成部分?

简单地讲,Java内存模型约定线程之间共享的变量存储在主内存中,而每个线程又有自身私有的本地内存(工作内存),本地内存是Java线程直接能读/写到的区域。 

3.volatile关键字的作用?

  1. 1)在对变量进行写入时,会先写入到本地内存然后立即刷新到主内存中; 2)在对变量读取时,直接从主内存读取。
  2. volatile标识的共享变量不会被指令重排序。  

4.什么是指令重排序

重排序是指编译器和处理器为了优化程序性能而对指令序列的执行顺序进行重新排列的一种手段。

5.指令重排序必须满足什么条件

  • as-if-serial语义:即无论怎么样重排序,都不能够改变单线程程序运行的结果。例如:
 double a = 1.0; // 1
 double b = 2.0; // 2
 double result = a * b; // 3

经过指令重排序后,可能被优化成以下:

double b = 2.0; // 2
double a = 1.0; // 1
double result = a * b; //3

1和2的执行顺序被调换,但是不会影响单线程下,执行3获得的结果。

  • 数据依赖性:即无论怎么样重排序,都不能够改变具有数据依赖性的两个操作的执行顺序。

【数据依赖性:如果两个操作同时访问一个变量,且这两个操作中有一个为写操作,那么这两个操作具有数据依赖性。】

double a = 1.0; // 1
double b = a * 2.0; // 2
double result = a * b;

例如上述代码,不会被重排序调换1和2的位置,因为这两条指令都对a进行了操作,且1对a进行了写操作,存在数据依赖性。

6.指令重排序会对多线程造成影响吗

会。因为指令重排序只保证在单线程下的结果的一致性,但是在多线程下,若共享变量不存在数据依赖性,那么可能会造成不一样的结果,如以下代码:

    int a = 0;
    boolean flag = false;
    
    public void writer() {
        a = 1;          // 1
        flag = true;   // 2
    }
    
    public void reader() {
        if (flag) { // 3
            int i = a * a; // 4
            System.out.println("读取到的a为" + a); 
        }
    }

正常顺序执行过程,应该是1->2->3->4,读取到的a为1; 在上述代码中,由于a和flag不存在数据依赖性,所以指令1和2可能会被重排序。

在单线程环境下,由于as-if-serial语义,虽然执行顺序变成了2在1前面,但是仍然会得到一样的结果:

2->1->3->4,读取到的a为1

在多线程环境下,可能会发生以下问题:

线程A执行writer(): 2->    ->1
线程B执行reader():    3->4     ,读取到的a为0

上述问题,只需要在a和b之前使用volatile修饰就可以解决了,因为使用volatile可以避免指令的重排序。那么执行顺序只能是1->2->3->4。

7.什么是happens-before关系

  • 定义:

happens-before关系即先行发生关系,它提供了线程操作可见性的保证。

  1. 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,并且第一个操作的执行顺序排在第二个之前。
  2. 如果两个操作之间存在happens-before关系,并不意味着Java平台的具体实现需要按照happens-before关系指定的顺序来执行。

8.happens-before和as-if-serial语义之间的区别

  1. as-if-serial语义保证了单线程内程序的执行结果不会被重排序改变。
  2. happens-before关系保证了正确同步的多线程程序的执行结果不会被改变。

9.happens-before规则

happens-before有如下规则用以保证它的先行发生可见性:

  1. 程序顺序规则:一个线程中的每个操作都happens-before后续操作。
  2. volatile变量规则:对一个volatie变量的写都happens-before后续的读。
  3. 传递性规则:如果A happens-before B,B happens-before C,那么A happens-before C。
  4. 监视器规则:如果A获取到了锁,那么B需要在A释放锁后方可获取锁。
  5. 线程启动原则: A线程开启了B线程,那么A线程在调用start()方法之前的代码 happens-before B线程。
  6. 线程终止原则:A线程开启了B线程,且A线程等待B线程的执行(b.join()),那么B线程所有操作都happens-before A。

8.双重检查加锁下的重排序问题

一个错误的双重检查加锁Demo

public class Singleton {
    private Singleton instance = null;
    private Singleton() {}
    
    public Singleton getInstance() {
        // 先判断对象是否已经初始化
        if (instance == null) {
            synchronized (instance) {
                // 确定对象没有初始化
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

上述代码发生错误的原因在于return new Singleton()这行代码,这行代码并不是一个原子性的的操作,它可以分为以下三个阶段:

1.分配对象内存
2.初始化对象
3.将对象引用到内存空间

其中,在将对象引用到内存空间时,instance就不等于null了。

在单线程情况下,2和3指令发生重排序,但是由于as-if-serial语义,其执行结果也是不变的:

 

 

在多线程情况下,有可能会发生以下的执行顺序,导致程序执行结果出错: A线程在执行到第3步的时候即设置instance指向内存空间,此时B线程判断其不为空,返回instance,但此时instance并未进行初始化,所以出现了错误,返回了一个空对象。

 

解决该问题的方法是在instance前加上volatile修饰,这样就能禁止重排序。 

 

使用MID360在室内快速定位 在预构建的点云地图中实现实时3D全球定位。通过融合FAST-LIO的低频全局定位(约0.5~0.2Hz)和高频里程计,整个系统的计算效率很高。消除里程计的累计误差初始定位可以通过RVIZ的粗略手动估计或来自另一个传感器/算法的姿势来提供。 阅读详情

相关推荐

无人机信号干扰系统设计方案

设计定向阻塞干扰器需要结合射频(RF)技术、功率放大技术和定向天线技术,核心目标是向特定方向发射高功率噪声信号,覆盖目标频段,阻断无人机的通信或导航信号。实际设计中需平衡功率、效率、散热和便携性,同时需考虑抗干扰技术的快速演进(如无人机加密通信和AI避障)。在合法框架下,此类设备可有效应对“黑飞”威胁,但技术滥用风险需严格管控。定向阻塞干扰器的硬件设计核心在于。

wanghao312的博客 3061

SSH框架搭建之实现并配置DAO

SSH框架搭建之实现并配置DAO

weixin_46741943的博客 472

【 Git学习记录(三)】推荐四款主流的Git代码托管平台 —— GitHub、GitLab、Gitee、Coding

了解过Git之后,作为一个程序猿最基本的,你还需要了解目前主流的在线代码托管平台,并至少选择其中一款在平时开发中使用。这里特别介绍以下四款: 国外平台 GitHub —— 全球最流行的代码托管平台 GitLab —— 基于git的仓库管理程序 国内平台 Gitee —— 开源中国旗下的代码托管平台 Coding —— 支持社交元素的代码托管平台 一. GitHub 链接地址:https://github.com/ GitHub目前是全球最流行的代码托管平台(别称:全球最大的同性交友.

JZevin的博客 6093

​SSH 框架学习Java中的Action、Dao、Service、Model-

​ SSH 框架学习之初识Java中的Action、Dao、Service、Model- ​

qq_46940224的博客 1001

避开SiC MOSFET驱动设计的3个大坑:电流/电阻/电压配置全指南

本文深入解析SiC MOSFET驱动设计中的三大关键参数:驱动电流、栅极电阻和工作电压,揭示常见设计误区与优化方案。通过实际案例与数据对比,指导工程师避开第三代电力电子半导体应用中的典型陷阱,提升系统可靠性与效率。特别针对驱动电源配置、栅极电阻选型等核心问题提供可落地的解决方案。

weixin_29214199的博客 385

python指纹识别算法(亲测代码可用)

python指纹识别算法

2301_76482796的博客 3079

三大框架整合ssh(二)------DAO层 源码

一、最高层的DAO接口(ICommonDao.java) public interface ICommonDao { public void save(T t); } 二、最高层的DAO接口的实现(CommonDaoImpl.java) public class CommonDaoImpl extends HibernateDaoSupport implements IC

你究竟是想一辈子卖糖水,还是希望获得改变世界的机会? 4407

Openwifi 开源项目解读(一)

Openwifi wifi 80211 无线网卡 基带 FPGA linux 驱动 开源

2万+

SSH框架之dao层

package cn.itcast.oa.base;import java.util.List; public interface BaseDao<T> { /** * 保存实体 * @param entity */ void save(T entity); /** * 删除实体 * @param id */

baidu_15113429的博客 1941

STEP 7-Micro/WIN SMART 编程软件:从入门到精通的使用指南

通过不断实践,熟悉软件的各项功能和操作技巧,工程师可更好地应对工业自动化控制中的各种挑战,开发出高效、稳定的控制程序。项目的基本组件是构成程序的核心元素,包括:程序块(主程序 OB1、子程序和中断程序)、数据块(给 V 存储器赋初值)、系统块(硬件组态和参数设置)、符号表(用符号代替存储器地址,使程序更易理解)以及状态图表(监视、修改和强制变量状态)。下载程序时,单击工具栏上的 “下载” 按钮,在弹出的下载对话框中选择要下载的块(程序块、数据块、系统块),单击 “下载” 即可。

电工 5575

电脑技巧:Hyper-V安装UOS操作系统图文教程

今天给大家分享微软官方Hyper-v虚拟机管理软件安装UOS操作系统图文教程,感兴趣的朋友也可以下载试一试,当然有啥问题也可以互相沟通交流!

IT技术分享社区 4707

SSH中公共Dao层、Dao接口

早做项目时候!为了优化代码可以创建公共类与接口 先写一个Dao层的公共接口,在里面实现增删改查等各种功能 public interface IBaseDao { //增加 public void save(T entity); //删除 public void delete(Long id); //修改 public void update(T entity);

wan23333的博客 1473

Ambari 3.0+BigTop 3.3集群部署实战

Ambari 3.0+BigTop 3.3集群部署

docsz的博客 612

Java并发编程系列之三:Java内存模型

引言 在我们日常项目开发工作中,对于并发场景中经常出现的奇妙的Bug大都是由于变量的可见性、原子性以及有序性的不深入理解导致的。那我们该如何解决并发编程者三座大山呢,这就需要请出我们本文讨论的对象Java内存模型了。 并发编程三大特性说明 volatile原理 Java内存模型 总结 一、并发编程三大特性说明 1、原子性 所谓原子性操作,就是类似数据库的事务操作,对应的一个操作或者多个操作,要...

481

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

面试官:为什么需要happens-before规则和什么是指令重排序

本篇文章从happens-before定义、用途以及具体规则三个方面对happens-before进行解读,并通过源码案例深入了解为什么需要happens-before规则和什么是指令重排序。 什么是happens-before happens-before:A happens-before B就是A先行发生于B(这种说法不是很准确),定义为hb(A, B)。在Java内存模型中,happens-before的意思是前一个操作的结果可以被后续操作获取。 为什么需要happens-before ..

java3456的博客 347

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

《Java并发编程的艺术》读书笔记——Java内存模型

第三章 Java内存模型3.1 内存模型基础3.1.1 并发编程的两个关键问题 线程之间如何通信 java采用共享内存模型隐式通信 线程之间如何同步 共享内存模型模型需要显式指定同步 3.1.2 内存模型抽象结构3.1.3 从源代码到指令序列的重排序3.1.5 happens-beforeJSR-133内存模型采用了happens-before概念,指前一个操作执行的结果对后一个操作可见,且前一

书翔星空 1215

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

JVM-java内存模型及java关键字内存语义

什么是java内存模型,就我理解,可以如上图所示。首先,因为java的内存模型是一个二级缓存,线程首先需要将数据写入到本地缓存,然后才能将本地缓存的数据刷到主内存中,所以就出现了可见性的和有序性的问题。由于cpu会进行优化,会进行cpu级别指令重排序。并且在java前端编译器中,也会对java代码进行优化,也有可能对代码进行重排序,这就导致了有序性问题。为了解决上述问题,jmm是怎么做的呢?jmm会禁用掉部分指令重排序。同时针对java的二级缓存带来的问题,java会在部分指令之间加入内存屏障。

zhifou123456的博客 976

《深入理解 Java 内存模型》读书笔记

《深入理解 Java 内存模型》读书笔记

SpringForAll的博客 906

Java并发编程实战基础概要

Java线程之间的通信由Java内存模型(JMM)控制。JMM定义了线程和主内存之间的抽象关系在内存里,Java内存模型规定了所有的变量都存储在主内存(物理内存)中,每条线程还有自己的工作内存,线程对变量的所有操作都必须在工作内存中进行。不同的线程无法访问其他线程的工作内存里的内容。我们可以使用下图来表示在逻辑上线程、主内存、工作内存的三者交互关系。额外的延伸知识,从JVM的角度来看java运行时的内存区域,来对应我们Java内存模型前面我们都理解了缓存导致了可见性问题,编译优化导致了有序性问题。.....

C. 785

Java内存模型(JMM)详解

Java内存模型(Java Memory Model简称JMM)是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。

EverythingAtOnce的博客 1996

java中volatile关键字_Java面试官最爱问的volatile关键字

在Java相关的岗位面试中,很多面试官都喜欢考察面试者对Java并发的了解程度,而以volatile关键字作为一个小的切入点,往往可以一问到底,把Java内存模型(JMM),Java并发编程的一些特性都牵扯出来,深入地话还可以考察JVM底层实现以及操作系统的相关知识。 下面我们以一次假想的面试过程,来深入了解下volitile关键字吧!面试官: Java并发这块了解的怎么样?说说你对volatil...

weixin_30871011的博客 190

java中volatile的作用_java中volatile关键字的作用

一.内存模型的相关概念大家都知道,计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。因此在C...

weixin_30613239的博客 161

Volatile关键字

一、Volatile关键字说明Volatile是Java提供的轻量级同步工具,它能保证可见性和做到禁止指令重排做到有序性,但是它不能保证原子性,如果你的程序必须做到原子性的话,那么可以考虑使用JUC的原子包下的原子类或者加锁的方式来保证,但是我们假设如果使用volatile来修饰共享变量,那么它能够保证的是一个线程对它所修饰的变量进行更改操作后总是能对其他线程可见,如下: 对于如上代码,虽然我们对成员变量i使用volatile修饰,意味着,对于执行addOne()方法的任意线程来说,看到这个变量的值是一

qq_37080455的博客 460

java内存模型和并发

java内存模型和并发 皮皮虾我们走 C++ rookie 34 人赞了该文章 前言 为了更好的理解java并发编程,特地补充一篇关于java内存模型也即JMM的分享。 PS:近期个人撸了一个微服务框架,欢迎想了解微服务实现的同学指正。 kingtang/gamma 随着单个处理器频率的提升越来越困难,人们转向了多核理器,同时这么多年来致力于提高程序的运行效率,然而面向多核处理...

evilcry2012的专栏 248

JAVA多线程-(五)并发编程三大特性之有序性volatile

与

qq_37200262的博客 728

JMM(Java 内存模型)详解

JMM(Java 内存模型)详解

qq_31849075的博客 298
上一篇: Ajax异步配合数据字典完成下拉选框
下一篇: 常用设计模式总结
BoringRong
BoringRong 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄9年 2万+粉丝 386原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BoringRong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值