一、指令重排序
1.javac生成字节码时,及字节码被JIT编译器编译成本地代码时,会进行指令的重排序
2.处理器可以乱序和并发执行指令
3.如果没有充分同步,在另一个线程中完全可以看到一个线程代码执行顺序和“理所当然”的顺序不一致
以上的程序,可能会打印出x=0,y=0;x=1,y=0;x=0,y=1;x=1,y=1
4.要使程序的执行可以预测,JMM必然会做一些规定限制,那就是happens-before规则,final初始化规则
二、happens-before
1.是一种偏序关系
2.规则:
(1)同一个线程中的每个action都happens-before于出现在其后的任何一个action
(2)对一个监视器的解锁happens-before于每一个后续对同一个监视器的加锁
(3)对volatile字段的写入操作happens-before于每一个后续的同一个字段的读操作
(4)Thread.start()的调用会happens-before于启动线程里面的动作
(5)Thread中的所有动作都happens-before于其他线程检查到此线程结束或者Thread.join()中返回或者Thread.isAlive()==false
(6)一个线程调用另一个线程的interrupt happens-before于被中断的线程发现中断(通过抛出InterruptedException,或者调用isInterrupted和interrupted)
(7)一个对象构造函数的结束happens-before与该对象的finalizer的开始
(8)如果A动作happens-before于B动作,而B动作happens-before与C动作,那么A动作happens-before于C动作
3.代码分析:
a)利用规则(1)、(3)和(8)分析
b)result=v < releaseShared(0)
c)acquireSharedInterruptibly(0) < return result
d)在releaseShared(0)、 acquireSharedInterruptibly(0)中会操作AQS中的一个volatile变量
e)releaseShared(0) < acquireSharedInterruptibly(0)
f)因此,result不设置为volatile类型也可以具有volatile类型的可见性
三、final初始化规则
1.当构造函数执行完成后,对象创建正确,无论怎样发布,所有线程都能看到final域的值,并且任何通过final域触及到的变量(一个final的数组中的元素,一个final的HashMap中的元素),也都是可见的
2.保证了不变对象可以通过任何方式发布
四、double-check lock
1.起源于为了改善synchronized的单例的性能
2.由于外层的resource未进行同步,因此即使resource的引用可见了,但是resource的状态不一定可见,因此可能一个线程读到resource并未构造成功
3.改进:Holder模式,利用了在ClassLoader加载类时才会初始化类的static成员
五、ABA问题
(1)cas中,内存V的值由A改为B,又改为A,那么按照compareAndSet(T oldValue, T newValue),由于内存V没有版本号,所以cas会成功,因为忽略过程来看,内存V的值没有变化还是A
(2)AtomicStampedReference利用版本化方案,替代AtomicReference解决这一问题