什么是内存模型
- JMM(Java内存模型)规定了JVM必须遵循一组最小保证,这组保证规定了对变量的写入操作在何时将对其他线程可见。
-
JMM为程序中所有的操作定义了一个偏序关系,称为Happens-Before。两个操作缺乏Happens-Before关系,则Jvm会对它们进行任意的重排序。
Happends-Before的规则包括:
1. 程序顺序规则。若程序中操作A在操作B之前,则线程中操作A在操作B之前执行。
2. 监视器锁规则。在同一监视器锁上的解锁操作必须在加锁操作之前执行。如图所示,
3. volatile变量规则。对volatile变量的写操作必须在读操作之前执行。
4. 线程启动规则。Thread.start必须在线程中执行的操作之前被调用。
5. 线程关闭规则。线程中的任何操作必须在其他线程检测到该线程结束之前执行,或者从Thread.join中返回,或调用Threas.isAlive返回false。
6. 中断规则。当一个线程在另一个线程上调用interrupt时,必须在被中断线程检测到interrupt调用之前执行。
7. 终结器规则。对象的构造函数必须在启动该对象的终结器之前执行完成。
8. 传递性。如果操作A在操作B之前执行,操作B在操作C之前执行,则操作A必须在操作C之前执行。
双重检查加锁
- /**
- * 双重检查加锁, 不安全,
- * 线程可能看到无效的值, 可加上volatile修饰
- */
- public class DoubleCheckedLocking {
- private static Object resource;
- public static Object getInstance(){
- if (resource == null){
- synchronized (DoubleCheckedLocking.class){
- if (resource == null){
- resource = new Object();
- }
- }
- }
- return resource;
- }
- }
如果不使用volatile,可能看到对象的错误或者无效状态。
原因:因为JMM会在发布对象的引用和内部构造函数之间进行重排序,也就是说先把栈中对象的引用指向堆中的位置,但是由于重排序堆中的构造函数还没有执行完。但是使用volatile可以避免这种重排序。
因为接下来要看《Java并发编程的艺术》,里面有大量的章节讲述内存模型的只是,而这本书的这一章节比较晦涩,所以就不细看了。