23种设计模式之单例模式的各类写法

单例模式

单例模式是23种设计模式中最基础的设计模式之一,用于保证某个类只产生一个实例对象,下面介绍单例模式的各类写法以及它们的优缺点



一、饿汉式

public class Singleton01 {
          
   
	private Singleton01() {
          
   
	}
	private static final Singleton01 INSTANCE = new Singleton01();
	public static Singleton01 getInstance() {
          
   
		return INSTANCE;
	}
}

此方式实现简单,并由JVM保证了线程安全。唯一的缺点就是类加载后就进行了对象初始化,而不是在需要用到此对象时才进行对象初始化,即延迟加载。

二、懒汉式

1.普通饿汉式

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

此方式实现了延迟加载的需求,却带来了线程安全的问题。例如,线程A通过 if (INSTANCE == null)的判断,但此后失去了CPU的执行权,线程阻塞于此。此时假如线程B也调用了getInstance()方法,并通过判断创建了Singleton02实例对象,线程B执行结束。随后,线程A恢复CPU执行权接着往下执行,又会创建一个新的Singleton02()实例对象。因此,在这种情况下单例模式会被破坏。

2.双重检查锁(DCL)

public class Singleton03 {
          
   
    private Singleton03() {
          
   
    }
    private static volatile Singleton03 INSTANCE; //使用volatile禁止指令重排序
    public static Singleton03 getInstance() {
          
   
        if (INSTANCE == null) {
          
   
            synchronized (Singleton03.class) {
          
   
                if (INSTANCE == null) {
          
   
                    INSTANCE = new Singleton03();
                }
            }
        }
        return INSTANCE;
    }
}

此方式针对普通懒汉式的线程安全问题进行了解决,进行了两次 (INSTANCE == null) 的判断以及使用volatile关键字禁止了指令重排序。其中内层(INSTANCE == null)判断解决了普通懒汉式中的线程安全问题,外层(INSTANCE == null)判断避免已经产生对象后线程也会进入同步代码块加锁造成资源浪费的情况。


3.静态内部类

public class Singleton04 {
          
   
    private Singleton04() {
          
   
    }
    private static class Singleton04Holder {
          
   
        private final static Singleton04 INSTANCE = new Singleton04();
    }
    public static Singleton04 getInstance() {
          
   
        return Singleton04Holder.INSTANCE;
    }
}

此方式既实现了线程安全又实现了延迟加载。线程安全方面,由JVM来进行保证。延迟加载方面,当Singleton4类加载时,内部类Singleton04Holder不会被加载,其只有在外部调用getInstance()方法时才会被加载。

4.枚举类

public enum Singleton05 {
          
   
    INSTANCE;

    public void show(){
          
   
        System.out.println(Singleton05.INSTANCE.hashCode());
    }
}
经验分享 程序员 微信小程序 职场和发展