单例模式的五种实现方式

1.懒汉式

线程不安全:最基础的实现方式,线程上下文单例,不需要共享给所有线程,也不需要加synchronize之类的锁,以提高性能

package com.yjc.singleton;
/**
 * 单例之懒汉式
 * */
public class LazySingleton {
    //构造私有化
    private LazySingleton()
    {

    }
    //类加载的时候不进行初始化
    private  static LazySingleton lazySingleton=null;


    public  static LazySingleton getLazySingleton(){
        //当调用者需要获取一个对象的时候,首先判断当前的对象是否已经进行过实例化了,
        if (lazySingleton!=null){
            //当多个线程同时进入此处的时候,就无法保证是单例的了,因此这种方式是线程非安全的
            lazySingleton=new LazySingleton();
        }
        return lazySingleton;
    }

}

2.饿汉式

类加载的时候进行初始化,典型的以空间换时间,线程是安全的,无法做到延迟加载

package com.yjc.singleton;
/**
 * 单例之饿汉式
 * */
public class HungrySingleton {
    //构造私有化
    private HungrySingleton()
    {

    }
    private  static HungrySingleton hungrySingleton=new HungrySingleton(); //类加载的时候直接实例化

    public  static  HungrySingleton getHungrySingleton()
    {
        return hungrySingleton;
    }
}

3.双重锁懒汉模式(Double Check Lock)

双重校验是懒汉式的升级版,通过加锁实现了线程安全,并同时具备延迟加载的机制

package com.yjc.singleton;
/**
 * 单例之双重校验(懒汉式的线程安全版)
 * */
public class DoubleCheckSingleton {
    //构造方法私有化
    private  DoubleCheckSingleton()
    {

    }
    //volatile用于保证内存可见性,所有线程都能看到共享内存的最新状态
    private  static volatile DoubleCheckSingleton doubleCheckSingleton=null;
    
    public  static  DoubleCheckSingleton getDoubleCheckSingleton(){
        if (doubleCheckSingleton!=null){
            //synchronized保证同时只能有一个线程进行实例化对象
            synchronized (DoubleCheckSingleton.class){
                if (doubleCheckSingleton!=null){
                    doubleCheckSingleton=new DoubleCheckSingleton();
                }
            }
        }
            return doubleCheckSingleton;
    }

}

第一次判断doubleCheckSingleton== null为了避免非必要加锁,当第一次加载时才对实例进行加锁再实例化。这样既可以节约内存空间,又可以保证线程安全

在JDK1.6及之后volatile可以解决DCL失效问题,volatile确保单例对象每次均在主内存中读取,这样虽然会牺牲一点效率,但也没有太多影响

4.静态内部类

静态内部类的优点是:在外部类被加载的时候,内部类并不会被立即加载,内部类没有被加载,单例对象也就没有进行实例化,从而也不会占内存。只有在第一次访问内部类中的属性时才会加载内部类

并将内部类中的对象进行实例化。这种方法不仅可以确保线程的安全和对象唯一,也延迟了单例对象的实例化

package com.yjc.singleton;
/**
 * 单例之静态内部类
 * */
public class StaticInnerClassSingleton {

    //构造私有化
    private  StaticInnerClassSingleton()
    {

    }
    //静态内部类,外部类被加载时,内部类不会被加载
    private  static  class StaticInnerClass
    {
        // 静态初始化器,由JVM来保证线程安全
        private  static StaticInnerClassSingleton staticInnerClassSingleton=new StaticInnerClassSingleton();
    }

    public  static  StaticInnerClassSingleton getStaticInnerClassSingleton()
    {
        //此时开始加载内部类,并将对象进行实例化
        return StaticInnerClass.staticInnerClassSingleton;
    }
}

5.枚举

枚举的特性:枚举实例不仅是线程安全的,而且在任何情况下它都是一个单例,枚举的属性都是静态常量。

枚举单例可以自己处理序列化:传统的单例模式的另外一个问题是一旦你实现了serializable接口,他们就不再是单例的了,因为readObject()方法总是返回一个 新的实例对象,就像java中的构造器一样。你可以使用readResolve()方法来避免这种情况

package com.yjc.singleton;

public class Singleton {
        //私有化构造
        private Singleton() {
        }

        public static Singleton getInstance() {
            
            return SingletonEnum.INSTANCE.getInstance();
        }

        private enum SingletonEnum {
            INSTANCE;

            private Singleton singleton;

            // JVM保证这个方法绝对只调用一次
            SingletonEnum() {
                singleton = new Singleton();
            }

            public Singleton getInstance() {
                return singleton;
            }
        }
}
原文地址:https://www.cnblogs.com/yjc1605961523/p/12074093.html