单例模式的5种写法

702 阅读2分钟

饿汉式

public class Singleton {
	
    private static String instance = new Singleton();
    
    private Singleton() {}
    
    public static Singleton getInstance() {
    	return instance;
    }
}

饿汉式以空间换时间。预先创建好实例,不会有线程安全的问题。但是,会浪费内存空间。

懒汉式

public class Singleton {

	private static String instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
    	if(instance == null) {
        	instance = new Singleton();
        }
        return instance;
    }
}

懒汉式以时间换空间,在要使用的时候才实例化对象。但这个版本的饿汉式是存在线程安全问题的。在多线程情况下,推荐使用双检锁。

双检锁

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

双检锁,也叫双重校验锁(Double Checked Locking, DCL)。解决了线程安全的问题,也比直接上锁提高了执行效率,也节省了空间。

静态内部类

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

外部类加载时,内部类并不会立刻加载。只有当getInstance方法被调用的时候,内部类才会被加载,并且初始化实例。这种方法不仅延迟了实例的加载,而且还保证了单例的唯一性,也能保证线程安全。

枚举实现单例

public enum Singleton {
	INSTANCE;
    
    public Singleton getInstance() {
    	return INSTANCE;
    }
}

业内大神Joshua Bloch说过:"单元素的枚举类型已经成为实现Singleton的最佳方法"。原因如下:

  • 将构造器私有化并不保险
  • 序列化前后对象并不相等,这个不符合单例模式的定义(可以解决)

而枚举实现的单例模式可以避免这些问题。

这篇文章重点在于单例模式的写法,而不是为什么。所以这里只是简单介绍了一下各种写法的优缺点。

对于 单元素的枚举类型已经成为实现Singleton的最佳方法 这句话的理解,我这里就不阐述了,感兴趣的可以自行上网查找。