并发编程的两个问题:缓存导致的不可见,编译优化导致的有序性问题
解决:禁用缓存,禁用编译优化。但是总不能所有程序都禁用吧,这样程序的性能就堪忧了。
Java内存模型:按需禁用缓存,禁用编译优化。
从本质上来讲,Java内存模型规范了JVM如何提供按需禁用缓存和编译优化的方法。具体来说,这些方法包括volatile、synchronized、和final三个关键字,以及七项happens-before规则。
一、volatile
它修饰的变量,告诉编译器,对这个变量的读写,不能使用cpu缓存,必须从内存中读取或写入。
二、synchronized
管程,在Java中指的就是synchronized。在进入代码块之前,会自动加锁,在代码块执行完会自动释放锁。
三、final
它修饰的变量,告诉编译器:这个变量生而不变,可以可劲儿优化。
happens-before规则:
1、程序的顺序性规则
指在一个线程中,按照程序顺序,前面的操作happens-before于后续的任意操作
2、volatile变量规则
指 对一个volatile变量的写操作,happens-before于后续对这个volatile变量的读操作。
3、传递性
如果A happens-before于B,B happens-before于 C,那么A happens-before 于 C。
4、管程中锁的规则
即synchronized代码块happens-before于后续进入该代码块以及后续的所有操作。
5、线程start()规则
线程A启动子线程B,那么子线程B能够看到线程A在启动线程B前的操作。
6、线程join()规则
这条是关于线程等待的。在线程A中调用线程B的join()并成功返回,那么线程B中的任意操作happens-before于该join()操作的返回。
7、线程终止规则
(1).线程终止规则:线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测到线程已经终止执行。
(2).线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测到是否有中断发生。
四、Java内存模型底层怎么实现的?
主要是通过内存屏障(memory barrier)禁止重排序的,即时编译器根据具体的底层体系架构,将这些内存屏障替换成具体的 CPU 指令。对于编译器而言,内存屏障将限制它所能做的重排序优化。而对于处理器而言,内存屏障将会导致缓存的刷新操作。比如,对于volatile,编译器将在volatile字段的读写操作前后各插入一些内存屏障。