java并发编程实战《一》可见性、原子性和有序性
可见性、原子性和有序性问题:并发编程Bug的源头
核心矛盾:CPU、IO、内存三者之间的速度差异。
为了合理利用 CPU 的高性能,平衡这三者的速度差异,计算机体系结构、操作系统、编译程序都做出了贡献,主要体现为:
1.CPU 增加了缓存,以均衡与内存的速度差异;
2.操作系统增加了进程、线程,以分时复用 CPU,进而均衡 CPU 与 I/O 设备的速度差异;
3.编译程序优化指令执行次序,使得缓存能够得到更加合理地利用。
缓存导致的可见性问题

一个线程对共享变量的修改,另外一个线程能够立刻看到,我们称为可见性。
有必要提一下的是,一个线程如何修改共享变量?
线程有自己的工作内存,可概称为栈(出自《深入理解java虚拟机》第二版,12.3.1 主内存与工作内存),而你的数据是存在主存中的,线程修改数据时先从主存中拷贝你的数据复制到自己的内存中修改,然后写回主存。
如果两个线程同时做上述操作,就引发了可见性问题。
线程切换带来的原子性问题
所谓的原子性问题即原子操作,即不会被线程调度机制打断的操作。
比如这个样子

在一个时间片内,如果一个进程进行一个 IO 操作,例如读个文件,这个时候该进程可以把自己标记为“休眠状态”并出让 CPU 的使用权,待文件读进内存,操作系统会把这个休眠的进程唤醒,唤醒后的进程就有机会重新获得 CPU 的使用权了。
这里的进程在等待 IO 时之所以会释放 CPU 使用权,是为了让 CPU 在这段等待时间里可以做别的事情,这样一来 CPU 的使用率就上来了;此外,如果这时有另外一个进程也读文件,读文件的操作就会排队,磁盘驱动在完成一个进程的读操作后,发现有排队的任务,就会立即启动下一个读操作,这样 IO 的使用率也上来了。
是不是很简单的逻辑?但是,虽然看似简单,支持多进程分时复用在操作系统的发展史上却具有里程碑意义,Unix 就是因为解决了这个问题而名噪天下的。

编译优化带来的有序性问题
例如java双重检查锁
1 public class Singleton {
2 static Singleton instance;
3 static Singleton getInstance(){
4 if (instance == null) {
5 synchronized(Singleton.class) {
6 if (instance == null)
7 instance = new Singleton();
8 }
9 }
10 return instance;
11 }
12 }
假设有两个线程 A、B 同时调用 getInstance() 方法,他们会同时发现 instance == null ,于是同时对 Singleton.class 加锁,此时 JVM 保证只有一个线程能够加锁成功(假设是线程 A),另外一个线程则会处于等待状态(假设是线程 B);线程 A 会创建一个 Singleton 实例,之后释放锁,锁释放后,线程 B 被唤醒,线程 B 再次尝试加锁,此时是可以加锁成功的,加锁成功后,线程 B 检查 instance == null 时会发现,已经创建过 Singleton 实例了,所以线程 B 不会再创建一个 Singleton 实例。
这看上去一切都很完美,无懈可击,但实际上这个 getInstance() 方法并不完美。问题出在哪里呢?
出在 new 操作上,我们以为的 new 操作应该是:
1.分配一块内存 M;
2.在内存 M 上初始化 Singleton 对象;
3.然后 M 的地址赋值给 instance 变量。
但是实际上优化后的执行路径却是这样的:
1.分配一块内存 M;
2.将 M 的地址赋值给 instance 变量;
3.最后在内存 M 上初始化 Singleton 对象。
优化后会导致什么问题呢?我们假设线程 A 先执行 getInstance() 方法,当执行完指令 2 时恰好发生了线程切换,切换到了线程 B 上;如果此时线程 B 也执行 getInstance() 方法,那么线程 B 在执行第一个判断时会发现 instance != null ,所以直接返回 instance,而此时的 instance 是没有初始化过的,如果我们这个时候访问 instance 的成员变量就可能触发空指针异常。

课后思考
常听人说,在 32 位的机器上对 long 型变量进行加减操作存在并发隐患,到底是不是这样呢?
long类型64位,所以在32位的机器上,对long类型的数据操作通常需要多条指令组合出来,无法保证原子性。
摘自极客时间王宝令老师的课程
java并发编程实战《一》可见性、原子性和有序性的更多相关文章
- Java并发编程实战 01并发编程的Bug源头
摘要 编写正确的并发程序对我来说是一件极其困难的事情,由于知识不足,只知道synchronized这个修饰符进行同步. 本文为学习极客时间:Java并发编程实战 01的总结,文章取图也是来自于该文章 ...
- Java并发编程实战 02Java如何解决可见性和有序性问题
摘要 在上一篇文章当中,讲到了CPU缓存导致可见性.线程切换导致了原子性.编译优化导致了有序性问题.那么这篇文章就先解决其中的可见性和有序性问题,引出了今天的主角:Java内存模型(面试并发的时候会经 ...
- Java并发编程实战 03互斥锁 解决原子性问题
文章系列 Java并发编程实战 01并发编程的Bug源头 Java并发编程实战 02Java如何解决可见性和有序性问题 摘要 在上一篇文章02Java如何解决可见性和有序性问题当中,我们解决了可见性和 ...
- 【java并发编程实战】-----线程基本概念
学习Java并发已经有一个多月了,感觉有些东西学习一会儿了就会忘记,做了一些笔记但是不系统,对于Java并发这么大的"系统",需要自己好好总结.整理才能征服它.希望同仁们一起来学习 ...
- 《Java并发编程实战》/童云兰译【PDF】下载
<Java并发编程实战>/童云兰译[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062521 内容简介 本书深入浅出地介绍了Jav ...
- 《java并发编程实战》笔记
<java并发编程实战>这本书配合并发编程网中的并发系列文章一起看,效果会好很多. 并发系列的文章链接为: Java并发性和多线程介绍目录 建议: <java并发编程实战>第 ...
- 《Java并发编程实战》文摘
更新时间:2017-06-03 <Java并发编程实战>文摘,有兴趣的朋友可以买本纸质书仔细研究下. 一 线程安全性 1.1 什么是线程安全性 当多个线程访问某个类时,不管运行时环境采用何 ...
- Java并发编程实战 04死锁了怎么办?
Java并发编程文章系列 Java并发编程实战 01并发编程的Bug源头 Java并发编程实战 02Java如何解决可见性和有序性问题 Java并发编程实战 03互斥锁 解决原子性问题 前提 在第三篇 ...
- Java并发编程实战 05等待-通知机制和活跃性问题
Java并发编程系列 Java并发编程实战 01并发编程的Bug源头 Java并发编程实战 02Java如何解决可见性和有序性问题 Java并发编程实战 03互斥锁 解决原子性问题 Java并发编程实 ...
- Java并发编程实战——读后感
未完待续. 阅读帮助 本文运用<如何阅读一本书>的学习方法进行学习. P15 表示对于书的第15页. Java并发编程实战简称为并发书或者该书之类的. 熟能生巧,不断地去理解,就像欣赏一部 ...
随机推荐
- springboot data jdbc 数据库日期和查询出来的结果不一致
解决方法: 一.将serverTimezone=UTC改为CTT url: jdbc:mysql://localhost:3306/moviechoicesystem?serverTimezone=C ...
- python爬虫07BeautifulSoup
有一个高效的网页解析库 它的名字叫做 BeautifulSoup 它 是一个可以从 HTML 或 XML 文件中提取数据的 Python 库 首先我们要安装一下这个库 pip install be ...
- STM32最小系统板OLED贪吃蛇
上次用STM32F103最小系统板做了一个简单的OLED贪吃蛇小游戏,以下为游戏效果动图: 主要实现内容包括:贪吃蛇移动.方向控制.食物生成.分数处理.死亡判定. 这次想把自己的制作思路分享给大家,不 ...
- nat+端口转发,使得宿主机secureCRT可以访问vbox里linux虚拟机
环境:vbox或者叫vitrualbox连接虚拟机,由于公司内网不能分配IP(不知道是不是这个原因),虚拟机用桥接得不到IP,没法实现虚拟机和宿主互相访问,于是用NAT. 遗憾:NAT是能连接网络,也 ...
- KMP算法和bfprt算法总结
目录 1 KMP算法 1.1 KMP算法分析 1.2 KMP算法应用 题目1:旋转词 题目2:子树问题 2 bfprt算法 2.1 bfprt算法分析 2.2 bfprt算法应用 1 KMP算法 大厂 ...
- 'sortbitwise'是什么意思
问题 flag sortbitwise 在ceph中是什么意思,在Jewel版本下可以看到多了这个flags [root@lab8106 current]# ceph -s cluster ffe7a ...
- 从 3.1 到 5.0 —— OpenReservation 更新记
OpenReservation 从 asp.net core 3.1 到 5.0 Intro OpenReservation 是一个开源的预约系统,最初的版本是我们学校的活动室预约系统,现在正逐步变成 ...
- SSL加密原理
对称加密算法 对称加密算法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密. 非对称加密算法 非对称加密算法(RSA)是内容加密的一类算法,它有两个秘钥:公钥与私钥 ...
- javascript布局转换
javascript布局转换是将原有的浮动布局通过JS里面的javascript属性 offsetLeft 和offsetTop默认值来给元素设置绝对定位. 原来的:缺点--我们想给网页中的图片添加拖 ...
- day96:flask:flask-migrate&flask-session&蓝图Blueprint&蓝图的运行机制
目录 1.flask-migrate 2.flask-session 3.蓝图:Blueprint 4.蓝图的运行机制 1.数据库迁移:flask-migrate 1.Flask的数据库迁移 在开发过 ...