跳到主要内容
  1. 所有文章/
  2. 23种设计模式笔记/
  3. 5种创建型设计模式/

单例模式

·📄 1305 字·🍵 3 分钟

应用场景 #

对于 Java来说,单例模式可以保证在一个 JVM 中只存在单一实例。单例模式的应用场景主要有以下几个方面。

  • 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
  • 某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
  • 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
  • 某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等
  • 频繁访问数据库或文件的对象
  • 对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套。
  • 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。

使用方式 #

饿汉式 #

先创建单例对象,不论现在是否需要,可能会浪费空间(如果对象所占的内存太大)。

/**
 * 饿汉式单例
 */
public class Hungry {

    /**
     * 可能会浪费空间
     */
    private byte[] data1=new byte[1024*1024];
    private byte[] data2=new byte[1024*1024];
    private byte[] data3=new byte[1024*1024];
    private byte[] data4=new byte[1024*1024];



    private Hungry(){

    }
    private final static Hungry hungry = new Hungry();

    public static Hungry getInstance(){
        return hungry;
    }

}

DCL懒汉式 #

//懒汉式单例模式
public class LazyMan {

    private static boolean key = false;//是否创建

    private LazyMan(){
        synchronized (LazyMan.class){
            if (key==false){
                key=true;
            }
            else{
                throw new RuntimeException("不要试图使用反射破坏异常");
            }
        }
        System.out.println(Thread.currentThread().getName()+" ok");
    }
    private volatile static LazyMan lazyMan;

    //双重检测锁模式 简称DCL懒汉式
    //执行双重检查是因为,如果多个线程同时了通过了第一次检查,
    //并且其中一个线程首先通过了第二次检查并实例化了对象,
    //那么剩余通过了第一次检查的线程就不会再去实例化对象。
    public static LazyMan getInstance(){
        //需要加锁
        if(lazyMan==null){  //1
            //减少进入等待锁的线程
            synchronized (LazyMan.class){   //2
                if(lazyMan==null){  //3
                    lazyMan=new LazyMan();  //4
                    /**
                     * a、分配内存空间
                     * b、执行构造方法,初始化对象
                     * c、把这个对象指向这个空间
 * 如果执行的过程是a->b->c的话,那上面的代码是没有问题的,
 * 但是有时JVM会基于指令优化的目的将指令重排,导致指令执行流程变为a->c->b。
 * 这样当线程A执行到4开始初始化单例对象的c流程时,线程B执行到1处,
 * 由于instance对象已经将内部指针指向分配的内存空间(即不为null),
 * 会直接返回未完全构造好的实例,从而出错。
                     *  我们就可以添加volatile保证禁止指令重排问题
                     */
                }
            }
        }
        return lazyMan;
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        //Java中有反射
//        LazyMan instance = LazyMan.getInstance();
        Field key = LazyMan.class.getDeclaredField("key");
        key.setAccessible(true);
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true); //无视了私有的构造器
        LazyMan lazyMan1 = declaredConstructor.newInstance();
        key.set(lazyMan1,false);
        LazyMan instance = declaredConstructor.newInstance();

        System.out.println(instance);
        System.out.println(lazyMan1);
        System.out.println(instance == lazyMan1);
    }
}

静态内部类方式 #

单例不安全, 因为反射

//静态内部类
public class Holder {
    private Holder(){

    }
    public static Holder getInstance(){
        return InnerClass.holder;
    }
    public static class InnerClass{
        private static final Holder holder = new Holder();
    }
}

枚举方式 #

使用枚举,我们就可以防止反射破坏了。

//enum 是什么? enum本身就是一个Class 类
public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

class Test{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        //java.lang.NoSuchMethodException: com.ogj.single.EnumSingle.<init>()

        EnumSingle instance2 = declaredConstructor.newInstance();
        System.out.println(instance1);
        System.out.println(instance2);
    }
}

枚举类型的最终反编译源码:

public final class EnumSingle extends Enum
{

    public static EnumSingle[] values()
    {
        return (EnumSingle[])$VALUES.clone();
    }

    public static EnumSingle valueOf(String name)
    {
        return (EnumSingle)Enum.valueOf(com/ogj/single/EnumSingle, name);
    }

    private EnumSingle(String s, int i)
    {
        super(s, i);
    }

    public EnumSingle getInstance()
    {
        return INSTANCE;
    }

    public static final EnumSingle INSTANCE;
    private static final EnumSingle $VALUES[];

    static 
    {
        INSTANCE = new EnumSingle("INSTANCE", 0);
        $VALUES = (new EnumSingle[] {
            INSTANCE
        });
    }
}

源码中的使用案例 #

org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry

持有了一个单例的适配器实例对象:

public final class GlobalAdvisorAdapterRegistry {

  private GlobalAdvisorAdapterRegistry() {
  }


  /**
   * Keep track of a single instance so we can return it to classes that request it.
   */
  private static AdvisorAdapterRegistry instance = new DefaultAdvisorAdapterRegistry();

  /**
   * Return the singleton {@link DefaultAdvisorAdapterRegistry} instance.
   */
  public static AdvisorAdapterRegistry getInstance() {
    return instance;
  }

  /**
   * Reset the singleton {@link DefaultAdvisorAdapterRegistry}, removing any
   * {@link AdvisorAdapterRegistry#registerAdvisorAdapter(AdvisorAdapter) registered}
   * adapters.
   */
  static void reset() {
    instance = new DefaultAdvisorAdapterRegistry();
  }

}