JMM:Java Memory Model(Java内存模型),围绕着在并发过程中如何处理可见性、原子性、有序性这三个特性而建立的模型。
可见性:JMM提供了volatile变量定义、final、synchronized块来保证可见性。
例如:线程a在将共享变量x=1写入主内存的时候,如何保证线程b读取共享变量x的值为1,这就是JMM做的事情。JMM通过控制主内存与每个线程的本地内存之间的交互,来为java程序员提供内存可见性保证。
原子性:JMM提供保证了访问基本数据类型的原子性(其实在写一个工作内存变量到主内存要两步:store、write),但是实际业务处理场景往往是需要更大的范围的原子性保证,所以模型也提供了synchronized块来保证
有序性:这个概念是相对而言的,如果在本线程内,所有的操作都是有序的,如果在一个线程观察另一个线程,所有的操作都是无序的,前句是“线程内表现为串行行为”,后句是“指令的重排序”和“工作内存和主内存同步延迟”现象,模型提供了volatile和synchronized来保证线程之间操作的有序性。
重排序:在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序(编译器、处理器),就是因为这些重排序,所以可能会导致多线程程序出现内存可见性问题(数据安全问题)和有序性问题。
JMM是如何处理的呢?
对于编译器,JMM的编译器重排序规则会禁止特定类型的编译器重排序。
对于处理器重排序,JMM的处理器重排序规则会要求java编译器在生成指令序列时,插入特定类型的内存屏障(memory barriers,intel称之为memory fence)指令,通过内存屏障指令来禁止特定类型的处理器重排序。
总之一句话,JMM是通过禁止特定类型的编译器重排序和处理器重排序来为程序员提供一致的内存可见性保证。
A线程具体什么时候刷新共享数据到主内存是不确定的,假设我们使用了同步原语(synchronized,volatile和final),那么刷新的时间是确定的,例如:线程A释放锁后会同步到主内存,线程B获取锁后会同步主内存数据。
即“A线程释放锁--B线程获取锁”可以实现A,B线程之间的通信。
JMM有以下主要的规则:
- An Unlock operation on a monitor synchronizes-with later lock operations. 当针对一个监控对象执行解锁操作时,首先synchronizes-with随后的锁操作。即当要解锁时,首先此操作会把它范围内的对象与主存同步,然后解锁,之后才会执行随后其他的锁操作。
- A write to a volatile variable synchronizes-with later reads of the variable. 当写一个volatile变量时,首先synchronizes-with随后的读此变量的操作。即当写完一个volatile变量后,JVM会首先将此变量与主存同步,然后才执行随后的读volatile变量操作。当然也从主存读了。
- If an action A synchronizes-with action B, then A happens-before B. 如果时行为A synchronizes-with 行为B,那么行为A happens-before B。
- If A comes before B in program order within a thread, then A happens-before B. 在同一个线程中如果程序中A在B之前,那么A也一定发生在B之前。
- The completion of a constructor happens-before the finalizer for that object starts to run. 完成构造函数会发生在此对象的finalizer启动之前,即在对一个对象回收前,此对象必须已经构造完毕。
- An action which starts a Thread synchronizes-with the first action of the new Thread. 创建了一个新线程的行为synchronizes-with这个线程的第一个操作。即创建完新线程后,JVM首先要把这个线程的信息同步给主存,然后才可以执行这个线程内部的其他操作。
- Thread.join() synchronizes-with the last (and all other) actions in the thread being joined. Thread.join()函数synchronizes-with被join的线程内部的所有其他操作。即执行Thread.join()之后JVM首先会把信息跟主存同步,然后才会继续执行被join线程内部的其他操作。
- (Transitivity) If X happens-before Y, and Y happens-before Z, then X happens-before Z. 传递性,即X在Y之前执行,Y在Z之前执行,那么X会在Z之前执行。
以上的这些规则是JMM所做的基本保证,在真正的JVM实现中会有更多更好的规则。