我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java指令重排在多线程环境下怎么解决

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java指令重排在多线程环境下怎么解决

本篇内容介绍了“Java指令重排在多线程环境下怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一、序言

指令重排在单线程环境下有利于提高程序的执行效率,不会对程序产生负面影响;在多线程环境下,指令重排会给程序带来意想不到的错误。

二、问题复原

(一)关联变量

下面给出一个能够百分之百复原指令重排的例子。

public class D {    static Integer a;    static Boolean flag;        public static void writer() {        a = 1;        flag = true;    }        public static void reader() {        if (flag != null && flag) {            System.out.println(a);            a = 0;            flag = false;        }    }}
1、结果预测

reader方法仅在flag变量为true时向控制台打印变量a的值。

writer方法先执行变量a的赋值操作,后执行变量flag的赋值操作。

如果按照上述分析逻辑,那么控制台打印的结果一定全为1。

2、指令重排

假如代码未发生指令重排,那么当flag变量为true时,变量a一定为1。

上述代码中关于变量a和变量flag在两个方法类均存在指令重排的情况。

public static void writer() {    a = 1;    flag = true;}

通过观察日志输出,发现有大量的0输出。

writer方法内部发生指令重排时,flag变量先完成赋值,此时假如当前线程发生中断,其它线程在调用reader方法,检测到flag变量为true,那么便打印变量a的值。此时控制台存在超出期望值的结果。

(二)new创建对象

使用关键字new创建对象时,因其非原子操作,故存在指令重排,指令重排在多线程环境下会带来负面影响。

public class Singleton {    private static UserModel instance;        public static UserModel getInstance() {        if (instance == null) {            synchronized (Singleton.class) {                if (instance == null) {                    instance = new UserModel(2, "B");                }            }        }        return instance;    }}@Data@AllArgsConstructorclass UserModel {    private Integer userId;    private String userName;}
1、解析创建过程
  • 使用关键字new创建一个对象,大致分为一下过程:

  • 在栈空间创建引用地址

  • 以类文件为模版在堆空间对象分配内存

  • 成员变量初始化

  • 使用构造函数初始化

  • 将引用值赋值给左侧存储变量

2、重排序过程分析

针对上述示例,假设第一个线程进入synchronized代码块,并开始创建对象,由于重排序存在,正常的创建对象过程被打乱,可能会出现在栈空间创建引用地址后,将引用值赋值给左侧存储变量,随后因CPU调度时间片耗尽而产生中断的情况。

后续线程在检测到instance变量不为空,则直接使用。因为单例对象并为实例化完成,直接使用会带来意想不到的结果。

三、应对指令重排

(一)AtomicReference原子类

使用原子类将一组相关联的变量封装成一个对象,利用原子操作的特性,有效回避指令重排问题。

@Data@NoArgsConstructor@AllArgsConstructorpublic class ValueModel {    private Integer value;    private Boolean flag;}

原子类应该是解决多线程环境下指令重排的首选方案,不仅通俗易懂,而且线程间使用的非重量级互斥锁,效率相对较高。

public class E {    private static final AtomicReference<ValueModel> ar = new AtomicReference<>(new ValueModel());        public static void writer() {        ar.set(new ValueModel(1, true));    }        public static void reader() {        ValueModel valueModel = ar.get();        if (valueModel.getFlag() != null && valueModel.getFlag()) {            System.out.println(valueModel.getValue());            ar.set(new ValueModel(0, false));        }    }}

当一组相关联的变量发生指令重排时,使用原子操作类是比较优的解法。

(二)volatile关键字

public class Singleton {    private volatile static UserModel instance;        public static UserModel getInstance() {        if (instance == null) {            synchronized (Singleton.class) {                if (instance == null) {                    instance = new UserModel(2, "B");                }            }        }        return instance;    }}@Data@AllArgsConstructorclass UserModel {    private Integer userId;    private String userName;}

四、指令重排的理解

1、指令重排广泛存在

指令重排不仅限于Java程序,实际上各种编译器均有指令重排的操作,从软件到CPU硬件都有。指令重排是对单线程执行的程序的一种性能优化,需要明确的是,指令重排在单线程环境下,不会改变顺序程序执行的预期结果。

2、多线程环境指令重排

上面讨论了两种典型多线程环境下指令重排,分析其带来负面影响,并分别提供了应对方式。

  • 对于关联变量,先封装成一个对象,然后使用原子类来操作

  • 对于new对象,使用volatile关键字修饰目标对象即可

3、synchronized锁与重排序无关

synchronized锁通过互斥锁,有序的保证线程访问特定的代码块。代码块内部的代码正常按照编译器执行的策略重排序。

尽管synchronized锁能够回避多线程环境下重排序带来的不利影响,但是互斥锁带来的线程开销相对较大,不推荐使用。

synchronized 块里的非原子操作依旧可能发生指令重排

“Java指令重排在多线程环境下怎么解决”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java指令重排在多线程环境下怎么解决

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java指令重排在多线程环境下怎么解决

本篇内容介绍了“Java指令重排在多线程环境下怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、序言指令重排在单线程环境下有利于提高
2023-06-30

Java指令重排序在多线程环境下如何处理

这篇文章主要讲解了“Java指令重排序在多线程环境下如何处理”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java指令重排序在多线程环境下如何处理”吧!一、序言指令重排在单线程环境下有利于提
2023-06-30

java for循环内执行多线程问题怎么解决

这篇文章主要介绍了java for循环内执行多线程问题怎么解决的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇java for循环内执行多线程问题怎么解决文章都会有所收获,下面我们一起来看看吧。java用多线程来
2023-07-05

Spring在多线程下@Resource注入为null怎么解决

本篇内容主要讲解“Spring在多线程下@Resource注入为null怎么解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring在多线程下@Resource注入为null怎么解决”吧!
2023-06-29

如何深入理解Java多线程与并发框中重排序、屏障指令、as-if-serial规则

这篇文章将为大家详细讲解有关如何深入理解Java多线程与并发框中重排序、屏障指令、as-if-serial规则,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。一、重排序前篇文章已经讲了Java
2023-06-05

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录