Java并发学习二:编译优化带来的有序性问题导致的并发Bug
Java并发学习系列文章:Java并发学习-博客专栏
今天在学习极客时间专栏:《Java并发编程实战》
第一讲01 | 可见性、原子性和有序性问题:并发编程Bug的源头
中提到:
编译器及解释器的优化可能导致意想不到的 Bug
双重检验创建单例代码如下:
public class Singleton {static Singleton instance;static Singleton getInstance(){if (instance == null) {synchronized(Singleton.class) {if (instance == null)instance = new Singleton();}}return instance;}
}
“you look,当有多有线程调用getInstance()方法的时候,不管三七二十一,先让他们进来。如果instance 实例不为空,那最好了,直接return实例instance ,跟synchronized一点都扯不上关系,所以也不会影响到性能。这是双重检验中的第一次检验。”
“oh,I know,如果instance 是null的,就进入synchronized语句块,在synchronized语句块里面初始化对象。但为什么在synchronized语句中需要再次检查instance 实例是否为null?”
“这就是第二次检验了,当有多个线程通过第一次检验时,假设线程拿到锁进入synchronized语句块,对instance 实例进行初始化,释放instance .class锁之后,线程二持有这个锁进入synchronized语句块,此时又对instance 对象就行初始化。所以在这里进行第二次检验防止这种意外发生。”
但上面的代码在多线程下可能会出现Bug,是因为编译器对new
做了优化:
默认的new操作
:
1.分配一块内存 M;
2.在内存 M 上初始化 Singleton 对象;
3.然后 M 的地址赋值给 instance 变量。
但是,当我们编译的时候,编译器在生成汇编代码的时候会对流程顺序进行优化。优化的结果是有可能按照1-2-3顺序执行,也可能按照1-3-2顺序执行。
我们知道,执行完3的时候就instance 对象就已经不为空了,如果是按照1-3-2的顺序执行,恰巧在执行到3的时候(还没执行2),突然跑来了一个线程,进来getInstance()方法之后判断instance 不为空就返回了instance 实例。
此时instance 实例虽不为空,但它还没执行构造方法进行初始化。又恰巧构造方法里面需要对某些参数进行初始化。后来闯进来的线程糊里糊涂对那些需要初始化的参数进行操作就有可能报错奔溃了。”
解决方法是加上volatile
关键字,volatile
关键字可以禁止指令重排序
这个例子让我想起了很久之前在码农翻身上看到的一篇文章:《Java帝国之单例设计模式》
Java并发学习二:编译优化带来的有序性问题导致的并发Bug相关推荐
- Java多线程学习二十五:阻塞和非阻塞队列的并发安全原理||如何选择适合自己的阻塞队列?
阻塞和非阻塞队列的并发安全原理. 之前我们探究了常见的阻塞队列的特点,以 ArrayBlockingQueue 为例, 首先分析 BlockingQueue 即阻塞队列的线程安全原理,然后再看看它的兄 ...
- java多线程学习二、安全与不安全示例:12306买票和银行取钱、java内存模型、内存可见性、线程同步块和方法
文章目录 前言 1. 什么是块,分为几种 2. 静态块与构造块的区别 一. 举例说明:并发情况下,线程不安全 1. 示例1:unsafe12306取票 2. 示例2:unsafe银行取钱 二.线程不安 ...
- Java多线程学习二十九:AtomicInteger(原子类) 和 synchronized 的异同点?
原子类和 synchronized 关键字都可以用来保证线程安全,在本课时中,我们首先分别用原子类和 synchronized 关键字来解决一个经典的线程安全问题,给出具体的代码对比,然后再分析它们背 ...
- Java多线程学习二十七:AtomicInteger 在高并发下性能不好,如何解决?为什么?
AtomicInteger 在高并发下性能不好,如何解决?以及为什么会出现这种情况? 我们知道在 JDK1.5 中新增了并发情况下使用的 Integer/Long 所对应的原子类 AtomicInte ...
- Java多线程学习二十一:ConcurrentHashMap 在 Java7 和 8 有何不同
在 Java 8 中,对于 ConcurrentHashMap 这个常用的工具类进行了很大的升级,对比之前 Java 7 版本在诸多方面都进行了调整和变化.不过,在 Java 7 中的 Segment ...
- Java基础学习(二十七)之IO流
1. File 1.1 File类概述和构造方法(myFile中的com.itheima_01中的FileDemo01) File:它是文件和目录路径名的抽象表示 文件和目录是可以通过File封装成对 ...
- Java多线程学习(二)---线程创建方式
线程创建方式 摘要: 1. 通过继承Thread类来创建并启动多线程的方式 2. 通过实现Runnable接口来创建并启动线程的方式 3. 通过实现Callable接口来创建并启动线程的方式 4. 总 ...
- java泛型学习二:解惑通配符
package generic;import java.awt.Canvas; import java.util.ArrayList; import java.util.Collection; imp ...
- Java多线程学习二十六:原子类是如何利用 CAS 保证线程安全的?
什么是原子类,以及它有什么作用. 在编程领域里,原子性意味着"一组操作要么全都操作成功,要么全都失败,不能只操作成功其中的一部分".而 java.util.concurrent.a ...
最新文章
- python 打印皮卡丘_Python到底是什么?学姐靠它拿了5个offer
- ECCV2020 最佳论文提名 | 流感知技术
- 2021年春季学期-信号与系统-第一次作业参考答案
- Java将网络地址对应的图片转成本地的图片
- 软考系统架构师笔记-综合知识重点(二)
- 蓝星二开某微商城源码+搭建教程
- vue :key的说明 看到这文章,解决你的疑问
- vscode设置go-lint警告提示
- IOS创建静态库Cocoa Touch Static Library
- 基于springboot的多人聊天系统
- 灭屏取消过温减流功能
- 大地高、正高和正常高及高程异常
- SpringMVC工作原理及其流程
- 大数据应用场景有哪些?一篇文章告诉你
- OpenCASCADE:Linux平台上构建OCCT使用的第三方库
- Qt基于FFmpeg读取摄像头并进行H264编码
- 如何查SCI期刊的影响因子与排名
- Light Emitting Hindenburg(bitset运用)
- 淹没之城(Submerged).PC单机游戏 免费下载.虚幻4 打造
- 苹果ios音频的回声消除处理
热门文章
- RDKit | 基于SMILES查找化合物的MACCS密钥
- java事务写法_【Spring4】采用注释写法,事务回滚报错问题
- 港大徐爱民组研究助理招聘-内分泌代谢方向
- MPB:林科院袁志林组-​内生镰刀菌基因组染色体级别组装和注释
- Nature综述 | 种内多样性:解释微生物组中的菌株
- Science:固氮(The nitrogen fix)
- 不需要懂得编程,但却可以使用ggplot2画出论文级别的图?
- R语言偏相关或者部分相关性系数计算实战:使用psych包计算(Partial Correlation)偏相关系数、拟合回归模型使用两个回归模型的残差计算偏相关性系数
- R语言使用ggplot2包geom_jitter()函数绘制分组(strip plot,一维散点图)带状图(添加均值、中位数)实战
- python将sklearn的RocCurveDisplay结果与PrecisionRecallDisplay结果合成为一个图