什么是内存模型

  • 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之前执行。

    双重检查加锁

  1. /**
  2.  * 双重检查加锁, 不安全,
  3.  * 线程可能看到无效的值, 可加上volatile修饰
  4.  */
  5. public class DoubleCheckedLocking {
  6.     private static Object resource;
  7.  
  8.     public static Object getInstance(){
  9.         if (resource == null){
  10.             synchronized (DoubleCheckedLocking.class){
  11.                 if (resource == null){
  12.                     resource = new Object();
  13.                 }
  14.             }
  15.         }
  16.         return resource;
  17.     }
  18. }

如果不使用volatile,可能看到对象的错误或者无效状态。

原因:因为JMM会在发布对象的引用和内部构造函数之间进行重排序,也就是说先把栈中对象的引用指向堆中的位置,但是由于重排序堆中的构造函数还没有执行完。但是使用volatile可以避免这种重排序。

因为接下来要看《Java并发编程的艺术》,里面有大量的章节讲述内存模型的只是,而这本书的这一章节比较晦涩,所以就不细看了。

04-30 11:53