算法:单例

53 阅读2分钟

饿汉式:

  • 类加载时就初始化,浪费内存,不能延迟加载;
  • 基于 classloader 机制避免了多线程的同步问题,线程安全;
  • 没有加锁,调用效率高。
public class Singleton{
    private static Singleton INSTANCE = new Singleton();
    
    private Singleton(){}
    
    public Singleton getInstance(){
        return INSTANCE;
    }
}

懒汉式:是线程不安全的,但是可以做到延迟加载。

public class Singleton{
    private static Singleton INSTANCE;
    
    private Singleton(){}
    
    public static Singleton getInstance(){
        if(INSTANCE == null){
            INSTANCE = new Singleton();
        }
        return INSTANCE;
    }
}

双重校验锁:在加锁之前判断是否为空,可以确保 instance 不为空的情况下,不用加锁,可以直接返回。 加锁之后,还需要判断 instance 是否为空,是为了防止在多线程并发的情况下,会实例化多个对象。例如:线程 a 和线程 b 同时调用 getInstance 方法,假如同时判断 instance 都为空,这时会同时进行抢锁。假如线程 a 先抢到锁,开始执行 synchronized 关键字包含的代码,此时线程 b 处于等待状态。线程 a 创建完新实例了,释放锁了,此时线程 b 拿到锁,进入 synchronized 关键字包含的代码,如果没有再判断一次 instance 是否为空,则可能会重复创建实例。

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

静态内部类:

  • 利用了classloader机制来保证初始化 instance 时只有一个线程,线程安全;
  • 只有通过显式调用 getInstance 方法时,才会显式装载静态内部类,从而实例化instance,延迟加载。
public class Singleton{
    private static class InnerClass{
        public final static Singleton INSTANCE = new Singleton();
    }
    
    private Singleton(){}
    
    public static Singleton getInstance(){
        return InnerClass.INSTANCE;
    }
}

枚举:能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。但是不是延迟加载的。

public enum Singleton{
    INSTANCE;
}