单例设计模式(四种实现与优缺点)

日常编程中,我们在用到对象的时候经常会new一个对象来使用,但是对于部分常用的对象来说,频繁的创建与销毁会给我们的服务器带来不小的压力,所以很多时候我们需要创建单例对象来避免这种情况发生。

单例的创建大致分为四种:

  1. 饿汉式
  2. 懒汉式
  3. 静态内部类
  4. 枚举

饿汉式

代码实现:

class Singleton{

    //私有化构造器,外部无法new
    private Singleton(){ }
    
    //本类内部创建对象实例
    private final static Singleton1 instance = new Singleton1();

    //对外提供一个getInstance方法,返回本类创建的实例
    public static Singleton1 getInstance(){
        return instance;
    }

}
  • 优点:写法简单,而且避免多线程线程同步问题。
  • 缺点:类加载的时候就实例化,无法懒加载,如果后续没有用到这个实例就浪费了内存空间。

懒汉式

/**
 * 双重检查
 */
public class Singleton {
    //加上volatile防止指令重排,且保证当前线程创建的instance对其他线程的可见性
    private static volatile Singleton instance;

    private Singleton(){}  //私有化构造方法,无法new出实例

    public static Singleton getInstance(){
        if(instance == null){
            //如果有线程进入这个判断,则阻塞,进入同步代码块之后再次判断,有实例创建了就直接返回
            synchronized (Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 优点:懒加载,线程安全,效率较高
  • 确定:写法和理解都有一定的难度
  • 双重检查的意义:如果将synchronized直接放在getInstance()方法上,也可以解决线程安全的问题,但是同步了整个方法效率太低了。双重检查只在创建的时候才加锁,相当于是锁的细腻度优化,如果在synchronized中不加上if(instance == null)判断,有些进入上一个 if(instance == null)判断的线程可能会继续创建一个新的实例,双重检查就是为了防止这种情况发生。

静态内部类

/**
 * 静态内部类实现单例, 推荐使用
 */
public class Singleton {
    private Singleton(){}
    
    public static class SingletonInstance{
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance(){
        return SingletonInstance.INSTANCE;
    }
}
  • 类加载的时候,线程是安全的,所以使用静态内部类来创建单例对象,是线程安全的,而且该类只会被加载一次。静态内部类在类加载的时候不会被加载,只有调用了getInstance()方法访问了内部类的时候,才会被加载,所以也属于一种懒加载。

枚举

public class Singleton {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        instance1.getInstance();
        System.out.println(instance1 == instance2);
    }
}

enum Singleton{
    INSTANCE;
    public void getInstance(){
        System.out.println("这是单例需要执行的方法");
    }
}
  • java作者推荐的方法,但是枚举是饿汉式,无法懒加载
  • 枚举实现,通过反射和反序列化创建第二对象也不行,上面的几种实现方法可以通过反射获取新的实例,某种意义上不是真正的单例。

你可能感兴趣的:(设计模式)