并发编程 volatile单例延迟加载

  • Post author:
  • Post category:其他


https://github.com/yzmaodeng/java-keypointknowledge.git

@概念一:


有序性

编译器为了优化程序性能而采取的对指令进行重新排序执行的一种方式。指令重排虽然代码没有按照顺序执行,但是通过一定的机制来确保不影响运行的最终的结果。

一次单线程运行不会有太大的影响,但多线程的编程就不可忽视了。

@问题的抛出:那么我们就看看

双重检测

的延迟单例的逻辑会不会存在问题呢?



  1. public


    class

    ZLSingleton {


  2. private


    static


    ZLSingleton

    instance =

    null

    ;  ——》 0语句


  3. private


    ZLSingleton

    () {}

  4. if

    (instance ==


    null


    ) {

    ——》 1语句




  5. public


    static


    synchronized


    ZLSingleton

    getInstance() {

    ——》 2语句



  6. if

    (instance ==

    null

    ) {

    ——》 3语句


  7. instance =

    new


    ZLSingleton

    ();

    ——》 4语句


  8. }


  9. return

    instance;

  10. }

  11. }

  12. }

@@


instance =


new




ZLSingleton


(); 3语句执行过程呢的庖丁解牛的拆分




1@@@ 第一步获得主内存的空间的地址



3@@@ 将空间的地址赋给

instance 变量






2@@@调用够着函数初始化





(注意这里的期望的执行的顺序是1-2-3  但由于指令重排的可能,导致1-3-2)





@@两个线程模拟





线程1和线程2同时来到



——》 1语句 ,假设线程1获得cpu资源,线程2 处在等待获得锁的状态;



《问题浮现》

@结论:

Java 平台内存模型允许指令重排导致的这个结果

@解决方案

@@添加volitale(阻止指令重排

JDK5.0以后才可行



  1. public


    class

    ZLSingleton {


  2. private


    volatile


    static


    ZLSingleton

    instance =

    null

    ;


  3. private

    Singleton() {}


  4. public


    static


    ZLSingleton

    getInstance() {


  5. if

    (instance ==

    null

    ) {


  6. synchronized

    (

    ZLSingleton

    .

    class

    ) {


  7. if

    (instance ==

    null

    ) {


  8. instance =

    new


    ZLSingleton

    ();

  9. }

  10. }

  11. }


  12. return

    instance;

  13. }

  14. }

@@ 类的初始化锁机制(类初始化锁)


public class 单例Factory {


private static class Instancesovler {


public static Instance instance = new Instance();

}

public static Instance getInstance() {


return Instancesovler.instance ;  这里内部类初始化(锁)

}

}

@

总结



通过对比基于volatile的双重检查锁定的方案和基于类初始化的方案,我们会发现基于类初始化的方案的实现代码更简洁。但基于volatile的双重检查锁定的方案有一个额外的优势:除了可以对静态字段实现延迟初始化外,还可以对实例字段实现延迟初始化。


延迟初始化降低了初始化类或创建实例的开销,但增加了访问被延迟初始化的字段的开销。在大多数时候,正常的初始化要优于延迟初始化。如果确实需要对实例字段使用线程安全的延迟初始化,请使用上面介绍的基于volatile的延迟初始化的方案;如果确实需要对静态字段使用线程安全的延迟初始化,请使用上面介绍的基于类初始化的方案。



版权声明:本文为yz18931904原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。