单例模式是常用的设计模式之一,其定义如下:
单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。
单例模式有3个要点:
1.某个类只能有一个实例;
2.它必须自行创建这个实例;
3.它必须自行向整个系统提供这个实例。
单例模式的UML类图如下:

单例模式在代码实现上有一些差别,可以区分如下:
饿汉式单例:其中作为单例类的单实例的成员变量,在类初始化的时候,就实例化了这个单例对象,Java实现代码如下:
/**
* 单例模式(饿汉式)
*/
public class EagerSingleton {
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {
}
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
懒汉式单例:此时的单例对象并不在类初始化的时候实例化,而是在第一次使用这个单例对象的时候,即第一次调用getInstance方法的时候,单例对象才会实例化。这种实现方式相对于饿汉式来说,优点是:在不使用该单例对象的时候,不会创建单例对象,这样如果在单例类中实例变量非常多,类非常大的时候,比较节省系统空间,降低系统不必要的开销。缺点是在如果在多线程环境中获取单例对象时,会存在线程安全问题,也就是说可能会的到不同的多个对象,这违背了单例模式的初衷。但是懒汉式单例可以通过锁机制来解决这个问题,Java实现代码如下:
/**
* 单例模式(懒汉式:双重检查锁定DCL)
*/
public class LazySingleton {
private volatile static LazySingleton INSTANCE;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (INSTANCE == null) {
synchronized (LazySingleton.class) {
if (INSTANCE == null) {
INSTANCE = new LazySingleton();
}
}
}
return INSTANCE;
}
}
这里对懒汉式单例模式的代码实现解释三点:
1.之所以在第一个 if (INSTANCE == null)语句后加锁是因为如果在整个getInstance方法上加锁的化,无论INSTANCE变量是否为空,线程间都要去处理锁,降低了系统的性能;
2.之所以在锁的内部还要加上一次判断的判断的原因是,如果第一个线程正在创建对象,但还没有创建成功的时候,这个时候INSTANCE对象为null,这时第二个线程就进入了第一个if判断,并会等待第一个线程释放锁。然后第一个线程创建对象完成,并释放了锁,这时,第二个线程拿到锁并进入了锁的代码块,如果这里不再加一次判断的话,就会再次创建一个对象,出现了2个不同的对象。这种方法也叫做双重检查锁定(Double-Check Locking)DCL。
3.之所以要在实例变量INSTANCE上加上volatile 修饰符的原因是,创建对象不是一个原子性操作,它要分成多个指令来完成,比如:第一步,分配内存空间;第二步,执行构造方法,初始化成员变量;第三步,把对象指向这块内存空间。但是由于编译器或者处理器可能会对指令进行重排序,比如第一步分配内存空间,第二步把对象指向这块内存空间,第三步执行构造方法,初始化成员变量。这样的话,如果第一个线程前两步都执行完了,此时这个对象不为null。这时第二个线程进来发现对象不为null,直接返回了,但是此时对象内部的状态的不确定的,这时使用对象就可能会出现问题,因此需要加上volatile来防止指令重排序。
但是懒汉式单例模式增加了锁机制等,会降低系统的性能,饿汉式在一定情况下又会增加系统不必要的空间占用,这时我们可以使用静态内部类对饿汉式单例模式进行改进,使之在不使用的时候不去实例化对象,Java实现代码如下:
/**
* 单例模式(使用静态内部类)
*/
public class InnerClassSingleton {
private InnerClassSingleton() {
}
static class InnerClass {
public static InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance() {
return InnerClass.INSTANCE;
}
}
这样,Java虚拟机初始化外部类的时候,并不会实例化内部类的实例变量,只有在第一次使用内部类实例变量的时候才会去实例化,减少了系统不必要的开销。
单例模式详解
本文深入解析单例模式的定义、特点及其实现方式,包括饿汉式、懒汉式(双重检查锁定DCL)和静态内部类单例,探讨各种实现方式的优缺点及其适用场景。
1505

被折叠的 条评论
为什么被折叠?



