java多线程-volatile
大纲:
- java内存模型
- 可见性、原子性、有序性
- volatile关键字
- 例
一、java内存模型
java所有变量值都存在主内存里,每一个线程又拥有自己的工作内存,线程对变量的读写都在工作内存里完成,工作内存间相互隔离。
二、可见性、原子性、有序性
- 可见性:指当多个线程访问同一变量时,一个线程修改了这个变量的值,其他线程能访问这个变量是永远能够获取更新后的值。
保证可见性2种方式:1代码块加锁。2volatile关键字。
- 原子性:多个操作,要么全部执行,要么全部不执行。这些操作执行时不受外界干扰。
i = 1; //1 i = x; //2 i++; //3
//注意:上面的操作中,只有1是原子的。需要更大范围的原子操作需要加锁。
- 有序性:处理器会对指令进行重新排序,排序后的结果不会影响单线程结果,但有可能会影响多线程执行结果。指令重排会遵循happens-before 原则,其中volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作。就是说对一个变量,一个线程写,一个读,写操作一定在读操作前面。
三、volatile关键字
volatile相当于加入一个内存屏障,内存屏障保证3件事:
- 工作内存中修改的内容立即刷新到主内存。
- 当执行写操作时,其他工作内存中被写入的那个变量的缓存立即无效。
- 保证屏障后面的指令重排后出现在屏障前面,前面的指令不会到屏障后面。执行到内存屏障这条指令时,前面的代码都已经执行完毕。
a=1; b=2; test=true; //被volatile修饰 c=3; d=4; //test如果不被volatile修饰,指令重排可以将上面的5条赋值任意排序 //如果被volatile修饰,指令重拍只能在ab间重排、cd间重排而且执行test赋值时、ab赋值已经完成。
因此,volatile保证可见性,和一定的有序性,不保证原子性。
四、例
/** * 当flag不被volatile修饰的时候会出现init ok后thread2卡死的情况。 */ class TestVolatile { private static volatile boolean flag = false; public static void main(String[] args) throws InterruptedException { Thread thread2 = new Thread(()->{ while (true){ if(flag) System.out.println(1); } }); thread2.start(); Thread thread1 = new Thread(()->{ flag = true; System.out.println("init ok"); }); thread1.start(); } }