目录
介绍
本节我们来看看设计模式中使用场景最多的单例模式,网上也有很多的知识,各种单例模式的写法,对于我们日常的工作,已经足够了。单例模式的类必须保证该模式的对象只有一个实例的存在,许多时候,系统只需要拥有一个全局对象,这样有利于我们协调系统整体的行为,比如在一个应用中,应该只有一个ImageLoader实例,这个ImageLoader中又含有线程池、缓存系统、网络请求,很消耗资源,因此没有理由让它构造多个对象,这种不能自由构造对象的情况,就是单例模式的使用场景。
定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
使用场景
确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一个实例。例如,创建一个对象需要消耗的资源过多,像访问IO和数据库等资源,这时就需要考虑单例模式。
实现方式
1、懒汉模式
懒汉模式是声明一个静态对象,并且在用户第一次调用getInstance方法时进行初始化,示例代码如下:
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这种方式通过在getInstance方法上添加synchronized关键字保证同步,这也是保证对象唯一的手段,但是每次调用该方法都会同步,这样就会产生多余的资源消耗,所以一般不建议使用。
2、Double Check实现单例
DCL方式实现单例的优点是既能够在需要时才初始化,又能免保证线程安全,且单例对象初始化后每次调用getInstance可以不需要同步锁,代码如下:
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;
}
}
该方式的亮点就在getInstance方法中,可以看到该方法中对instance实例进行了两次判空,第一层主要是为了避免不必要的同步,第二层判断是为了首次进来时进行实例的创建。DCL模式能够在需要时才实例化对象,并且保证对象的唯一性,除非我们的代码在并发场景很复杂或者低于JDK6版本下使用,这种方式就能够满足我们的需求了。
3、静态内部类方式
DCL方式在一定程序上解决了资源消耗多、多余同步、线程安全的问题,但是某些场景下会出现失效的问题,这被称为双重锁定失效。静态内部类的实现方式如下:
public class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.sInstance;
}
private static class SingletonHolder {
private static final Singleton sInstance = new Singleton();
}
}
当第一次加载Singleton类时并不会初始化sInstance,只有一第一次调用getInstance方法时才会导致sInstance被初始化,因此第一次调用getInstance方法会导致虚拟机加载SingletonHolder类,这种方式不仅能够确保线程安全,而且能够保证对象唯一,同时也延迟了实例化,所以这是推荐使用的实现方式。
4、枚举方式
枚举方式的实现如下:
public enum Singleton {
INSTANCE;
public void process() {
}
}
此外还有容器方式,我们就不列举了,一般使用DCL或者静态内部类方式就可以了。