跳到主要内容

单例模式

  • 单例模式:保证整个系统中一个类只有一个对象的实例
  • 为什么使用单例模式
    1. 节省公共资源
    2. 方便控制:比如日志管理,如果多个实例同时写入日志,可能导致日志的错误,要控制日志的正确性,就必须对关键代码进行上锁,按照顺序写入。单例模式实现只有一个实例对日志进行写入,避免干扰

实现思路

  1. 构造私有:要保证一个类不能被多次实例化,就要阻止对象被 new 出来,因此需要把构造函数私有化
  2. 以静态方法返回实例:因为外界不能使用 new 获取对象实例,因此要通过提供类的方法来让外界获取对象实例
  3. 确保对象实例只有一个:只对类进行一次实例化,以后都直接获取第一次实例化的对象

单例模式的实现

饿汉式

public class Singleton {  
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
  • 特点:没有懒加载,线程安全
  • 优点:没有加锁,执行效率高
  • 缺点:类加载时就进行初始化,浪费内存

饿汉式-变种

public class Singleton {  
private static Singleton instance;

static {
instance = new Singleton();
}

private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
  • 跟饿汉式相同,但是将单例的创建挪到了静态块

懒汉式

public class Singleton {  
private static Singleton instance;
private Singleton (){}

public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
  • 特点:懒加载,线程不安全,不支持多线程

懒汉式-线程安全

public class Singleton {  
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
  • 特点:懒加载,线程安全,支持多线程
  • 优点:第一次调用才进行初始化,避免内存浪费
  • 缺点:加锁保证单例,影响效率

枚举式

public enum Singleton {  
INSTANCE;
}
  • 特点:最佳方式,简洁,自动支持序列化机制,JVM 保证不多次实例化,防止反射绕过

双重检验锁式

public class Singleton {  
// 为了避免多线程下的指令重排问题和多线程缓存造成的数据更新不及时问题,使用 volatile 禁止指令重排序
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
  • 特点:懒加载,线程安全,较为复杂,采用双锁机制,线程安全且多线程情况下保证高性能

内部静态类模式

public class Single {
private
Single(){
}

public static Single getSingle(){
return InnerClass.single;
}

// 静态内部类
public static class InnerClass{
private static final Single single=new Single();
}
}
  • 外部类没有 static 属性,则不会像饿汉式那样立即加载对象,只有真正调用 getInstance() 时,才会加载静态内部类,类加载时是线程安全的。instance 是 static final 类型,保证了内存中是有一个实例存在,并且只能被赋值一次,从而保证了线程安全,兼备了并发高效和延迟加载的优势

总结

  • 单例模式主要的实现方式
    • 饿汉式:线程安全,调用效率高,不能懒加载
    • 懒汉式:线程安全,调用效率不高,可以懒加载
  • 其他方式
    • 双重检验锁式:极端情况下偶尔会出现问题,不推荐使用
    • 静态内部类式:线程安全,调用效率高,可以懒加载
    • 枚举式:线程安全,调用效率高,不能懒加载