面试准备,同样是学习的过程,无法保证正确,慎,欢迎纠正
正文
JMM
-
原子性
-
原子性指一个操作是不可被中断的.jvm定义了8中原子操作:
-
lock:将主内存中的变量锁定,为一个线程所独占
-
unclock:将lock加的锁定解除,此时其它的线程可以有机会访问此变量
-
read:将主内存中的变量值读到工作内存当中
-
load:将read读取的值保存到工作内存中的变量副本中。
-
use:将值传递给线程的代码执行引擎
-
assign:将执行引擎处理返回的值重新赋值给变量副本
-
store:将变量副本的值存储到主内存中。
-
write:将store存储的值写入到主内存的共享变量当中。
-
-
-
一个很好的例子,i++;这个是操作系统课上必讲的内容.在操作系统的层面上,这个操作分为三个步骤,取出i的值,变量+1,重写回i的值.
-
还有一个例子,就是在32位系统下,对于long和double的读写不是原子的.是分两步进行的,如果是多线程情况下,可能会读到不存在的数字,这也是面试题.但是现在的系统已经解决这个问题.
-
可见性
- 因为计算机硬件和编译器优化的原因,会存在缓存和寄存器作为CPU和内存之间的中间件.在串行程序中,不存在可见性的问题,在并发情况下,指令执行的顺序无法预估,很可能发生一个线程修改了实际值,但是缓存中的值还未修改,却被另一个线程读到了缓存中的值,造成数据不一致.
- 这里其实是一个JVM主内存和工作内存的区别;
-
有序性
- 这个概念不难理解,但很难想到,至少我在接触并发的这些知识前,一度以为代码就是从前向后执行的,不是有顺序结构嘛.后来才知道重排序的事.
- 重排序是虚拟机和操作系统会对指令进行一定的重排,在保证不破坏语义的串行性的情况下,优化指令的执行顺序.重排序虽然不可知,但是有很多原则:
- 程序顺序原则:一个线程内保证语义的串行性
- volatile规则:volatile变量的写先于读发生,保证可见性
- 锁规则:解锁发生在另一个加锁前
- 传递性:A先于B,B先于C,A先于C
- 线程的start方法先于该线程的每一个动作
- 线程的所有操作先于线程的终结
- 线程的中断先于被中断的代码
- 构造函数的执行,结束先于finalize方法
- 并发情况下,一切情况都很难预料,实在无法理解,想想重排序的规则,也不用非要记住
volatile
- 这个词的意思是"易变的",“不稳定的”,当使用这个关键字修饰一个变量时,就等于告诉虚拟机,这个变量很可能被程序修改,为了保证这个变量的修改被所有线程看到,虚拟机会采用一些手段,保证可见性
- 比如我们上面说的重排序的规则之一:volatile变量的写先于读发生,保证有序性.保证可见性是通过每次访问刷新缓存,不会发生我们之前说到的,一个线程因为缓存和寄存器的原因读到未被写进内存的旧值.
- 很重要的一个点,volatile无法保证原子性,即使保证了可见性和有序性,但如果使用类似i++的操作,还是无法得到想要的结果.