设计模式之单例模式(一)

通常,对象通过在自身属性上执行任务来承担自己的职责,除了需要维护自身的一致性外,无须承担其他任何职责。
在某些场景,你需要找到一个承担职责的对象,并且这个对象是它所属类的唯一实例。

单例模式的意图是为了确保一个类有且仅有一个实例,并为它提供一个全局访问点。在有些系统中,为了节省内存资源、保证数据内容一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。

单例模式的结构

单例模式的主要角色如下。

  • 单例类:包含一个实例且能自行创建这个实例的类。
  • 访问类:使用单例的类。

其结构类如图所示:
单例模式结构图

单例模式的实现

单例模式有两种实现方式。

懒汉式

该模式的特点是类加载时没有生成单例,只有当调用实例方法时才创建。

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
private static volatile Singleton instance = null; //保证 instance 在所有线程中同步
private Singleton(){ //private 禁止在外部被实例化

}
public static synchronized Singleton getInstance(){ //getInstance 方法前加同步
if(instance == null){
instance = new Singleton();
}
return instance;
}
}

注意:如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。

饿汉式

该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。

1
2
3
4
5
6
7
8
9
public class Singleton{
private static final Singleton instance = new Singleton();
private Singleton(){

}
public static Singleton getInstance(){
return instance;
}
}

饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。

常见案例及应用场景

常见的单例模式:Spring框架中每个通过@Autowired注入的对象。
应用场景:

  • 某类只要求生成一个对象的时候。
  • 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
  • 当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。

小结

单例模式保证了类仅有一个实例,并为其提供了一个全局访问点。通过延迟初始化(仅在第一次使用时才初始化),一个单例对象是达到此目的的通用做法。在多线程环境下,必须小心管理线程间的写作,因为它们访问单例对象方法与数据的时间,可能只有毫厘之差。
对象具有唯一性,并不意味着使用了单例模式。单例模式通过隐藏构造函数,提供对象创建的唯一入口点,从而将类的职责集中在类的单个实例中。