单例模式
1. 介绍
单例模式,就是保证在整个软件系统中,对某个类只能存在一个对象实例 ,并且该类只提供一个能够取得其对象实例的方法(静态方法)。
单例模式可以节省系统资源,保证线程同步。
常用于经常创建和销毁的对象、创建和销毁耗时过多的对象、经常被使用的对象、工具类对象、频繁访问数据库或文件的对象。
2. 单例模式的多种方式
- 饿汉式
- 静态常量实现
- 静态代码块实现
- 懒汉式(线程不安全)
- 懒汉式(线程安全)
- 双重检查
- 静态内部类
- 枚举
2.1 饿汉式
静态常量的饿汉式中,直接通过new成员变量来创建单例的实例
静态代码块的饿汉式中,则是放在静态代码块中实例化的
步骤
- 构造器私有化(防止new),在类的内部创建对象
- 对外暴露一个静态公共方法获取类(
getInstance
方法)
优缺点
优点:写法简单,可避免线程同步问题
缺点:类加载时实例化,没有达到懒加载(Lazy Loading)的效果,存在资源浪费问题
2.2 懒汉式
判断单例对象是否为null,如果为null则实例化对象并返回,否则直接返回。
优缺点
优点:实现了懒加载,但是只能在单线程下使用
缺点:if (实例 == null) { 创建实例 }
这样的判断语句不是线程安全的,可能会产生多个实例
线程安全-同步方法
对getInstance
方法使用synchronized
关键字
优点:线程安全
缺点:效率低,实际开发不推荐
2.3 双重检查
优点:线程安全,多线程经常运用,效率较高
使用volatile
关键字,其作用是使被修饰的变量的修改可以立即同步到内存(可见性)
class Singleton {
private static volatile Singleton singleton;
private Singleton() {}
private static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
// 在你获取到锁之后,可能对象已经创建好了,需要再次判断
if (singleton == null) {
singleton == new Singleton();
}
}
}
return singleton;
}
}
2.4 静态内部类
静态内部类的特点:
- 外部类被加载时,内部类尚未被加载
- 静态内部类的属性初始化时,JVM保证线程安全
class Singleton {
private Singleton() {}
private static class SingletonInstance {
// SingletonInstance加载时实例化,线程安全
private static final Singleton INSTANCE = new Singleton();
}
private static Singleton getInstance() {
// 用的时候才加载SingletonInstance
return SingletonInstance.INSTANCE;
}
}
优点:线程安全,效率高
2.4 枚举
不仅能避免多线程同步问题,还能防止反序列化重新创建的对象
Effective Java的作者提倡的方式,推荐使用,但局限性大