并发学习笔记
并发学习笔记
线程不安全的原因
共享变量
线程对数据的操作是以一种类似数据缓存的方式来操作。内存模型中有主内存和本地内存的概念,注意,是概念,并不是一个固定的数据区域。线程操作变量只会操作本地内存里的数据,而从主内存读取和刷新到主内存是比较随机的,导致看似线程在访问同一个变量,实际是各玩各的。这一块叫做线程的可见性
指令重排
在程序的各个层面,比如编译器、jvm、操作系统、处理器都可能会对将要运行的执行进行一定的重排序,这种重排序主要是出于效率考虑。
重排序就是,你写的代码是执行方法A之后,执行方法B。实际运行过程中却非如此。在这一层面,比较看重的是原子性,任何非原子性操作就有可能发生指令重排,java中绝大多数操作都是非原子性的,包括但不限于new,++、+=,原子性操作有 =、CAS。
CAS
CAS全称比较并交换(compare and swap),需要三个操作数。第一个变量的地址,第二个期望中变量的值,第三个新值,当且仅当变量当前值等于期望值时,将变量赋予新值,否则什么都不做。这是一个原子操作。
JDK9以前需要通过UnSafe类实现CAS操作,9以后使用VarHandler实现
Volatile
两种功能:
- 禁止volatile变量本身的读写 和前后的操作之间的重排序
- 保证写入volatile会更新线程共享变量到主内存中(类似于缓存落库);保证读取volatile之前会从主内存获取共享变量(类似与缓存更新)。也就是保证变量在线程间的可见性
有以下程序
1 | |
由于jmm不保证 f 的内存可见性,死循环将永远无法结束
想要结束有以下方法
- 给f 加上volatile 关键字
- 此外 f 不变,额外加上一个 volatile 的变量 a,在循环体内读变量a进行读或者写操作,也能结束循环
- 循环体内执行一个有synchronized关键字的方法
以上方法的原理是强制线程从主内存里获取共享变量最新的值
并发学习笔记
http://blog.inkroom.cn/2021/05/29/212W51W.html