java设计模式——单例模式

单例模式的使用动机java

  某些系统中的某些类的对象只须要一个, 或者只能是一个, 若是多于一个甚至会出现错误,这时候咱们就要用到单例模式。日志对象, 打印池对象, 序列ID生成器等应用。单例模式可让系统在减小内存空间的状况下仍然能正常工做。安全

  生成单例的方式:1.懒汉式(线程不安全);2.懒汉式(线程安全);3.饿汉式;4.静态内部类;函数

  5.双重检验锁;6.枚举优化

这里只介绍下:双重检验锁。貌似如今都这个比较多。spa

其余单例模式能够参考:线程

https://www.zybuluo.com/pastqing/note/164787日志

http://cantellow.iteye.com/blog/838473code

双重校验锁
orm

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}

为何要判断两次singleton对象是否为空, 缘由仍是为了不多个对象实例的生成。假如线程A拿到锁进入同步块生成了一个对象并返回。此时线程B拿到锁进入同步块,若是此时不判断singleton的话,就会屡次建立对象, 形成单例失败。  volatile关键字,确保进程之间获取到的singleton是最新的。对象

        若是不在声明 Singleton singleton 加volatile,那么对于singleton = new Singleton不是一个原子操做。此时的双重锁的方式是有问题的:

    在执行new时JVM会发生如下几个动做:

    1.给singleton分配内存

    2.调用构造函数, 初始化成员变量

    3.将singleton对象指向所分配的内存空间 当第三步完成后,对象就再也不是null了。可是JVM会进行指令的优化重排序, 将本来的1-2-3的顺序可能变为1-3-2。这样就有可能在线程A实例化一个对象以后,线程B在3执行完后就抢占了,此时实例已经非Null此时线程二就会返回该实例, 这样就会出错了。 

    在JDK1.5以后,可使用volatile解决以上问题。

    关键字volatile能够说是java虚拟机提供的最轻量级的同步机制,它包含了两方面的语义:

    1、当一个变量定义为volatile时,它保证了此变量对全部线程的可见性。这里的可见性是指当一条线程修改了这个变量的值,新值对于其余线程来讲是能够当即知道的。而普通变量没法作到这一点。普通变量的值在线程间传递须要经过主内存来完成。

例如,线程A修改了一个普通变量的值,而后向主内存中回写,线程B等线程A回写完成后再次主内存进行读取时,新值才会可见。

2、禁止指令重排优化。普通变量仅仅会保证在该方法的执行过程当中全部依赖赋值结果的地方都能保证取到正确的结果,而不能保证变量赋值的操做顺序与代码一致。 

上面使用volatile来解决双重锁实现带来的问题, 正是运用了volatile的第二个语义。

相关文章
相关标签/搜索