java设计模式——单例模式(双层检查加锁)

  单例模式:创建一个独一无二,只能有一个实例的对象,并提供全局访问点。

  单例模式想必大家都不陌生,面试题出现频率颇高,但是要完全答对却不是那么容易,特别是涉及到多线程的时候,很容易绕晕。

一.饿汉模式

public class Singleton{
    //不管有没有用到,先创建实例
    private static Singleton singleton = new Singleton();
    // 私有构造方法
    private Singleton(){}
    //需要时直接返回
    public static Singleton getInstance(){
        return singleton;
    }
}

优点:这种写法是在类装载的时候就完成实例化,避免了线程同步问题,是线程安全的。

缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果,如果未使用过这个实例,则会造成内存的浪费。

二.懒汉模式

public class Singleton {
    private static Singleton singleton;
    // 私有的构造方法
    private Singleton(){}
    public static Singleton getInstance(){
        // 被动创建,在需要时才去创建
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

优点:这种写法起到了Lazy Loading的效果。

缺点:在多线程下,会产生多个实例,是线程不安全的,所以在多线程环境下不可使用这种方式。

三.改造懒汉模式(使之能够在多线程下使用)

1.加锁

public class Singleton {
    //多线程中变量需要加上volatile关键字,jdk1.5以上才支持
    private volatile static Singleton singleton;
    // 私有的构造方法
    private Singleton(){}
    //直接在方法上加上synchronized关键字
    public static synchronized Singleton getInstance(){
        // 被动创建,在需要时才去创建
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

这种方法确实可以达到目的,但是直接在方法上加锁,以后不论Singleton是否被创建,只要调用这个方法就会同步,影响性能,所以不建议这样做。

2.只在需要同步的地方加锁

public class Singleton {
    //多线程中变量需要加上volatile关键字,jdk1.5以上才支持
    private volatile static Singleton singleton;
    // 私有的构造方法
    private Singleton(){}
    public static Singleton getInstance(){
        // 被动创建,在需要时才去创建
        if (singleton == null) {                                                                           
            //只有创建实例的地方需要加锁
            synchronized(Singleton.class){          <-1                                                    
                singleton = new Singleton();        <-2
            }
        }
        return singleton;
   

如上所示,将同步锁加在此处虽然解决了每次都要调用同步锁的方法,但是这样做又会造成新的问题,会创建多个实例出来。

假设现在有两个线程(线程1,线程2),当线程1通过 if (singleton == null) 判断进入到<-1位置时,线程2获取了CPU执行权,此时线程1还未进入到同步方法,Singleton实例并没有被创建,所以singleton变量还是为null,线程2也进入到<-1位置,现在不论谁先拿到锁,都会将singleton = new Singleton();代码执行2次,因此会创建两个Singleton实例,所以还得继续改造。

3.双层检查加锁 

public class Singleton {
    //多线程中变量需要加上volatile关键字,jdk1.5以上才支持
    private volatile static Singleton singleton;
    //私有的构造方法
    private Singleton(){}
    public static Singleton getInstance(){
        //被动创建,在需要时才去创建
        if (singleton == null) {                                                                           
            //只有创建实例的地方需要加锁
            synchronized(Singleton.class){          <-1                                                    
                //这里再判断一次是否为null
                if (singleton == null) {            <-2
                    singleton = new Singleton();    <-3
                }
            }
        }
        return singleton;
   

在同步代码里再加一层非空判断,即使线程1和线程2都进入到了<-1位置,当其中一个线程创建了Singleton实例后,另一个线程执行到<-2时,就会返回fasle,这样就完美解决了懒汉模式线程不安全问题。

你可能感兴趣的:(java)