- 介绍
单例模式是应用最广的模式之一,也可能是很多初级工程师唯一会使用的设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需拥有一个全局对象,这样有利于我们协调系统整体的行为。
- 定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
- 使用场景
确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一个。例如,创建一个对象需要消耗的资源过多,需要访问IO和数据库等资源。
- 代码实现
饿汉单例模式:生命静态对象时就已经初始化instance,但是可能不需要它初始化,我们更希望在使用到单例的时候再初始化。
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
懒汉单例模式:在第一次调用getInstance时才初始化instance。但是第一次初始化之后,每次调用都会进行同步,这样会消耗不必要的资源。
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}
}
Double Check Lock(DCL):优点是既能够在需要时才初始化单例,又能够确保线程安全,且单例对象初始化后调用getInstance不进行同步锁。但是由于指令重排序问题,所以DCL会失效(解决方式可以为instance加上volatile关键字,从而禁止指令重排序)。
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
静态内部类单例模式:最推荐使用的单例模式。第一次加载Singleton类时并不会初始化instance,只有在第一次调用Singleton的getInstance方法时才会导致instance被初始化。因此,第一次调用getInstance方法会导致虚拟机加载SingletonHolder类,这种方式不仅能够保证线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化。
public class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
}
使用容器实现单例模式:在程序的初始,将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。
import java.util.HashMap;
import java.util.Map;
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<>();
private SingletonManager() {
}
public static void registerService(String key, Object instance) {
if (!objMap.containsKey(key))
objMap.put(key, instance);
}
public static Object getService(String key) {
return objMap.get(key);
}
}