并发编程的三个特性
- 原子性
- 一个操作或者多个操作,要不全部执行并且过程不可中断,要不全部不执行。
- 可见性
- 当多个线程访问同一个变量时,如果其中一个线程修改了这个变量的值,其他线程可以理解看到修改后的值。(Java 中提供了 volatile 关键字来保证了可见性)
- 有序性
- 程序的执行顺序按照代码的先后顺序执行
volatile 关键字
- volatile 是变量的修饰符。
- 修饰的变量的操作行为在内存中进行,不会产生副本,保证了共享变量的可见性(如果某一线程修改了该变量的值,这个值对于其他线程是立即可见的)。
- 禁止指令重排。
奈何,到这一次没有办法继续向下了,再向下就是指令了
Volatile 原理
-
当变量被声明为 volatile 类型后,在编译和运行的时候都会关注这个共享变量,然后此变量的操作不会与其他内存操作一起重排序。
-
被 volatile 修饰的变量在编译成字节码文件时会多一个 lock 指令,该指令在执行的过程中会产生相应的类似内存屏障的作用(内存屏障的作用在下面)。
- 在有内存屏障的地方,禁止指令排序(屏障后的代码不能与屏障前的代码交换执行顺序)
- 在有内存屏障的地方,共享变量被修改后会立即从本地内存写到主内存,并且让其他线程本地内存中该变量副本失效
-
volatile 关键字前会存在一个 lock: 前缀,他会对 CPU 总线和高速缓存加锁。
- 具体执行时,在 lock 锁住总线的时候,其他 CPU 的读写请求都会被阻塞,知道锁释放,最后释放锁后会把高速缓存中的脏数据全部刷到主内存,这个操作会使得在其他 CPU 里缓存了该地址的数据无效(缓存一致性协议)。
-
处理器、高速缓存、主内存间的交互关系
Java 工作内存和主存交互的几种原子操作
- lock :作用主内存,锁住主内存主变量
- unlock :作用于主内存,解锁主内存主变量
- read :作用于主内存,主内存传递数据到工作内存
- load :作用于工作内存,将主内存传递来的值赋给工作内存工作变量
- use :作用于工作内存,工作内存工作变量值传给执行引擎
- assign :作用于工作内存,引擎的结果赋值给工作内存工作变量
- store :作用于工作内存的变量,工作内存工作变量传送到主内存中
- write :作用于主内存的变量,工作内存传来的工作变量赋值给主内存主变量
- read and load :从主存复制变量到当前工作内存
- use and assign :执行代码,改变共享变量值
- store and write :用工作内存数据刷新主存相关内容
- 线程、主内存、工作内存三者的交互关系
volatile 关键字的应用
- 作为状态标志
- 独立观察
- 开销低的读写锁策略
总结
- 有些知识,只要不去深入了解,有可能这一生都不会知道,虽然学很多东西 50% 都是装逼使用的。