单例模式的六种实现方式

单例模式的六种实现方式

1、单例模式之饿汉式[可用]

//饿汉式实现单例模式
public class Hungry {
    
    

    private Hungry(){
    
    }

    private  static Hungry hungry = new Hungry();

    public static Hungry getHungry(){
    
    
        return hungry;
    }
}

优点:实现简单,类加载的时候就完成了实例化,避免了线程的同步问题
缺点:无法实现延迟加载,可能会造成内存的浪费(浪费可忽略)

2、单例模式之懒汉式[不可用]

//懒汉式实现单例模式
public class LazySingleton01 {
    
    

    private LazySingleton01(){
    
    }

    private static LazySingleton01 lazy ;

    public static LazySingleton01 getLazy(){
    
    
        if(lazy==null){
    
    
            return new LazySingleton01();
        }
        return lazy;
    }
}

缺点:线程不安全,不推荐使用

存在线程安全问题的过程:在运行过程中可能会存在这么一种情况,有多个线程去调用getLazy方法来获取LazySingleton01实例,就有可能发生,第一个线程在执行if(lazy = =null)的语句时,还没执行new LazySingleton01()时,第二个线程获得的lazy也为null,通过了(lazy==null)的判断,最终两个线程都会new实例。

3、单例模式之懒汉式+同步方法[不推荐使用]

//懒汉式+同步方法实现单例模式
public class LazySingleton02 {
    
    
    private LazySingleton02(){
    
    }

    private static LazySingleton02 lazy;

    public static synchronized LazySingleton02 getLazy(){
    
    
        if(lazy==null){
    
    
            return new LazySingleton02();
        }
        return lazy;
    }
}

线程安全,效率低不推荐使用

对于上述缺陷的改进可能有的人会想到如下的代码:

public class LazySingleton03 {
    
    
    private LazySingleton03(){
    
    }

    private static LazySingleton03 lazy;

    public static  LazySingleton03 getLazy(){
    
    
        if(lazy==null){
    
    
        	synchronized(LazySingleton03.class){
    
    
            	return new LazySingleton03();
            }
        }
        return lazy;
    }
}

该写法一样是线程不安全的,当一个线程还没有实例化LazySingleton03时,另一个线程执行到if(lazy==null)这个判断语句时就会进入if语句,虽然加了锁,但是等到第一个线程执行完lazy=new LazySingleton03()释放出这个锁时,另一个进入if语句的线程同样会实例化另外一个LazySingleton03对象。

经过一步步的探索,有了双重校验锁的懒汉式写法:

4、单例模式之懒汉式+双重校验锁[推荐使用]

//双重校验锁+懒汉式
public class DoubleCheckSingleton {
    
    
    private DoubleCheckSingleton(){
    
    }
    
    private volatile static DoubleCheckSingleton instance;
    
    public static DoubleCheckSingleton getInstance(){
    
    
        if(instance==null){
    
    
            synchronized (DoubleCheckSingleton.class){
    
    
                if(instance==null){
    
    
                    return new DoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}

优点:线程安全,延迟加载,效率较高

注意此处的volatile:

//双重校验锁+懒汉式
public class DoubleCheckSingleton {
    
    
    private Socket socket;

    private DoubleCheckSingleton(){
    
    
        this.socket=new Socket();
    }
    
    private volatile static DoubleCheckSingleton instance;
    public static DoubleCheckSingleton getInstance(){
    
    
        if(instance==null){
    
    
            synchronized (DoubleCheckSingleton.class){
    
    
                if(instance==null){
    
    
                    return new DoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}

根据JVM运行时指令重排序和Happens-Before的规则,instance和socket之间的实例化的顺序并没有什么关系约束,谁在前谁在后都有可能,如果instance最先实例化,那么socket就不会实例化,调用这个方法就会产生空指针异常------>

解决这个方法很简单,加一个关键字 volatite
这个关键字能够保证实例化的顺序的相对位置不变,instance实例化永远在socket之后

5、单例模式之静态内部类[推荐使用]

//静态内部类实现单例模式
public class StaticInner {
    
    

    private StaticInner(){
    
    }

    private static class inner{
    
    
        private static StaticInner instance=new StaticInner();
    }

    public static StaticInner getInstance(){
    
    
        return inner.instance;
    }
}

优点:避免了线程不安全,延迟加载,效率高
详解:这个方法并没有事先声明instance的静态成员,而是把它放到了静态内部类inner中,inner中定义了静态变量,并直接进行了实例化,当inner被主动引用的时候就会创建实例,StaticInner在实例创建过程中被收集到()方法中,这个方法是同步方法,
保证了内存的可见性,JVM指令的顺序执行和原子性。该方法也是单例模式的最好设计之一。

6、单例模式之枚举[推荐使用]

//枚举实现单例模式
public enum EnumSingleton {
    
    
    INSTANCE;

    private EnumSingleton(){
    
    }

    public void method(){
    
    }
}

借助JDK1.5中添加的枚举来实现单例模式
优点:不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,实现非常简单而且最安全可谓很完美。

猜你喜欢

转载自blog.csdn.net/AIHUAya/article/details/109242786