单例模式

是什么

单例模式是一个典型的创建型设计模式,核心思想是保证一个类只存在一个实例,并提供一个全局访问点来访问这个实例。

  • 只有一个实例是指在整个应用程序中只存在该类的一个实例对象,而不是创建多个相同类型的对象。
  • 全局访问点是指为了让其他类能够获取到这个唯一实例,该类要提供一个全局访问点,通常是一个静态方法,通过这个方法来获得这个唯一的实例。

为什么

简单来说,单例模式有以下几个优点让我们考虑来使用:

  1. 全局控制:只有一个实例的话可以严格控制客户端怎样访问它以及何时访问它,简单的说就是对唯一实例的受控访问。
  2. 节省资源:因为要保证只能存在一个实例,就避免了多次创建相同的对象,从而节省了系统资源,而且多个模块可以通过单例模式共享数据。
  3. 懒加载:单例模式可以实现懒加载,只有在需要的时候才进行实例化,这可以提高程序的性能。

基本要求

  1. 私有化构造函数,复制外部代码直接创建类的实例
  2. 私有静态实例变量,用来保存该类的唯一实例
  3. 共有的静态方法,提供一个全局的访问点以供其他地方获取唯一的实例

如何实现

单例模式的实现有多种,包括懒汉式、饿汉式等。

饿汉式是指在类的加载时就完成了实例的创建,不管创建之后需不需要马上使用,先创建再说,所以叫“饿汉”。

而懒汉式指的是在第一次获取该实例对象时,才创建该实例,已经创建好之后就直接返回,也就是需要的时候再创建。

在多线程环境中,懒汉式因为不会提前将对象创建好,所以可能会存在多个线程同时来获取该类的唯一实例,会存在现场安全问题,可能会导致创建多个实例,这种情况下我们可能采取一些同步机制,例如使用互斥锁来确保在任意时刻只有一个线程可以执行实例的创建。

饿汉式:

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

private Singleton1() {

}

public Singleton1 getInstance() {
return instance;
}
}

懒汉式,用双检索来保证线程安全同时提高程序性能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton2 {
private static volatile Singleton2 instance;

private Singleton2() {}

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

懒汉式中的实例使用volatile关键字修饰,它主要有两个作用:

1、保证可见性:volatile修饰的变量被修改时其他线程都可见,避免了线程间的数据不一致问题,因为volatile修饰的变量读写都会直接操作主内存,避免了线程本地内存和主内存数据不一致的问题。

2、禁止指令重排:重排序是指编译器和处理器为了优化程序性能面对指令序列进行重新排序的一种手段,有时候会改变程序予以的先后顺序。