定义
确保一个类只有一个实例,并提供一个全局唯一访问点,用以访问该实例。
模式类型
创建型设计模式
调用方式
单例模式的设计结构如上图,所有调用者可根据getInstance()方法,获得Single类的唯一实例,结构为多对一。
在Java单例模式的实现,应该注意以下事项:
- 为限制外部随意实例化Single类,Single类的构造方法应设置为private对外隐藏。
- 保证Single类有唯一的实例,Single实例需要在类中保存,那么Single实例对象需设置为private的私有变量,同时在多线程环境下的也应获取到唯一的实例。
- 为使其他类能够调用全局访问点获取唯一实例,那么getInstance()方法需要设置为public静态(类)方法。
实现方法
要想实现单例模式很简单,但要想实现兼具性能,线程安全的单例模式则需要做一些改动,目前有以下多种单例模式的实现。
饿汉式线程安全
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
优点:线程安全
缺点:无法达到lazy loading(延迟加载),资源浪费
懒汉式非线程安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
此方式的优点在于lazy loading(延迟加载)可节省资源,但是由于getInstance()方法访问未加锁, 会出现线程不安全的情况。
优点:lazy loading
缺点:线程不安全
懒汉式线程安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点:lazy loading,线程安全
缺点:synchronized导致性能较低
双重校验锁DCL
public class Singleton {
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;
}
}
优点:lazy loading,线程安全,高性能
缺点:实现较复杂
迷思小贴士:
双重效验锁为什么要在单例上加"volatile"关键词呢? 主要是为了禁止指令重排序,java中初始化一个实例(A a = new A())在java字节码中基本会有以下4个步骤: 1.申请内存空间 2.初始化默认值 3.执行构造器方法连接引用和实例 4.连接引用和实例 这4个步骤中会进行指令重排,1234 、1243都有可能,有可能造成未初始化完全的对象发布,导致其他线程访问到未初始化的对象,加入volatile可确保Java线程内存模型所有线程看到这个变量的值是一致的,同时还会禁止指令重排序。 |
登记式
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
优点:lazy loading,线程安全,高性能,实现简单
缺点:第一次加载时反应较慢,存在反序列化攻击或反射攻击
枚举
public enum Singleton {
INSTANCE;
public Singleton getInstance() {
return INSTANCE;
}
}
优点:lazy loading,线程安全,高性能,实现简单,自动支持序列化机制
缺点:未广泛使用
迷思小贴士:
根据effective java书中的建议,单例模式的最佳体验就是采用枚举模式,写法简单,利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题,枚举不存在反序列化问题,并且也能避免反射攻击。 |
使用场景
- 创建某个对象需要消耗过多的资源以及时间,则需要控制实例产生数量,达到节约资源的目的
- 频繁创建、销毁的对象
- 整个系统只允许存在一个对象,例如上下文环境、资源管理器、回收站等
- 一个实例的内部状态能被多个调用者共享时,避免多次创建性能损耗,例如数据库连接、日志文件、应用配置
- 方便资源相互通信,进行资源同步的环境,例如累加器,广播变量等