指令重排序 jmm
代码在底层都是一道道的指令,指令的执行顺序和代码的顺序不一定一致,但是最终的结果是一样的( 在单线程的情况下 ) 为了更加高效的执行代码。
多线程的情况下,指令的重排序就有可能会造成线程的不安全问题。
public class Test { static int a; static int b; static int x; static int y; public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(()->{ a=1; x=b; }); Thread thread2 = new Thread(()->{ b=2; y=a; }); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("x="+x+",y="+y); } }
输出的结果: x=0,y=1
x=2,y=0
当然还会出现其它的结果
【 出现不同的结果的原因:
多线程的各个线程执行的时机不一样
变量在主存和工作内存的值不一致
指令进行了重排序,在多线程的情况下,结果可能就会不一样
】
happens-before 原则:
happens-before 是 JMM 的核心概念。 JMM中 有 主内存和工作内存。 线程对变量的操作都是在工作内存中进行的。 在多线程的情况下就要考虑保证主内存和工作内存中的变量的值是一致的,否则就会出现脏数据。 通过加锁(同步) 或者 volatile 关键字来进行保证。
为什么会有happens-before原则?
为了保证程序最终的结果是确定的! (因为指令会发生重排序;因为有工作内存和主内存的存在,在多线程的情况下容易出错;)
什么是happens-before原则?
A操作happens-before B操作,那么A操作的结果对B操作可见,如果交换A操作和B操作的执行顺序对最终的结果没有影响,那么A和B操作的执行顺序也是可以交换的。
程序次序规则: 一个线程中的每个操作,happens-before于该线程中任意后续的操作 。同一线程的前面的写操作对后面的读操作可见。
监视器锁规则:对一个锁的解锁,happens-before 于后面对这个锁的加锁。
volatile变量规则:对于一个volatile变量的写,happens-before 后面对这个变量的读。
传递性:A 操作 happens-before B操作, B操作happens-before C操作, 那么A 操作 happens-before C 操作。
start规则: 如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。
join规则: 如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。
程序中断规则:对线程interrupted()方法的调用先行于被中断线程的代码检测到中断时间的发生。
对象finalize规则:一个对象的初始化完成(构造函数执行结束)先行于发生它的finalize()方法的开始。
怎么保证happens-before原则?
volatile:
保证了可见性和禁止指令重排序。不保证原子性。
浙公网安备 33010602011771号