1. 单例模式概述
1.1 单例定义
所谓类的单例设计模式, 就是采取一定的方法保证在整个的软件系统中, 对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
总结一句话,单例就是保证全局只有一个实例对象。
1.2 单例目的
使用单例可以避免重复创建对象,减少内存开销,提升系统性能
1.3 单例注意事项
- 单例模式保证了 系统内存中该类只存在一个对象, 节省了系统资源, 对于一些需要频繁创建销毁的对象, 使用单例模式可以提高系统性能。
- 当想实例化一个单例类的时候, 必须要记住使用相应的获取对象的方法, 而不是使用 new。
- 单例模式使用的场景: 需要频繁的进行创建和销毁的对象、 创建对象时耗时过多或耗费资源过多(即: 重量级对象), 但又经常用到的对象、 工具类对象、 频繁访问数据库或文件的对象(比如数据源、 session 工厂等)。
2.单例模式适用场景
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如:
1.需要频繁实例化然后销毁的对象。
2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
3.有状态的工具类对象。
4.频繁访问数据库或文件的对象。
以下都是单例模式的经典使用场景:
1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
2.控制资源的情况下,方便资源之间的互相通信。如线程池等。
3.单例八种方式详解
单例模式总共有八种实现方式,总体分为饿汉式,懒汉式,双重检查,静态内部类与枚举五中类型。
其中饿汉式有2种,懒汉式3种,双重检查,静态内部类与枚举各1种。
1.饿汉式(静态常量,不推荐)
优缺点说明:
1) 优点: 这种写法比较简单, 就是在类装载的时候就完成实例化。 避免了线程同步问题。
2) 缺点: 在类装载的时候就完成实例化, 没有达到 Lazy Loading 的效果。 如果从始至终从未使用过这个实例, 则
会造成内存的浪费
3) 这种方式基于 classloder 机制避免了多线程的同步问题, 不过, instance 在类装载时就实例化, 在单例模式中大
多数都是调用 getInstance 方法, 但是导致类装载的原因有很多种, 因此不能确定有其他的方式(或者其他的静
态方法) 导致类装载, 这时候初始化 instance 就没有达到 lazy loading 的效果
4) 结论: 这种单例模式可用, 可能造成内存浪费
2.饿汉式(静态代码块,不推荐)
优缺点说明:
1) 这种方式和上面的方式其实类似, 只不过将类实例化的过程放在了静态代码块中, 也是在类装载的时候, 就执
行静态代码块中的代码, 初始化类的实例。 优缺点和上面是一样的。
2) 结论: 这种单例模式可用, 但是可能造成内存浪费
3.懒汉式(线程不安全,不推荐)
优缺点说明:
1) 这种方式和上面的方式其实类似, 只不过将类实例化的过程放在了静态代码块中, 也是在类装载的时候, 就执
行静态代码块中的代码, 初始化类的实例。 优缺点和上面是一样的。
2) 结论: 这种单例模式可用, 但是可能造成内存浪费
4.懒汉式(线程安全, 同步方法,不推荐)
优缺点说明:
1) 解决了线程安全问题
2) 效率太低了, 每个线程在想获得类的实例时候, 执行 getInstance()方法都要进行同步。 而其实这个方法只执行
一次实例化代码就够了, 后面的想获得该类实例, 直接 return 就行了。 方法进行同步效率太低
3) 结论: 在实际开发中, 不推荐使用这种方式
5.懒汉式(线程安全, 同步代码块,不推荐)
不推荐使用
6.双重检查(推荐使用)
优缺点说明:
1) Double-Check 概念是多线程开发中常使用到的, 如代码中所示, 我们进行了两次 if (singleton == null)检查, 这
样就可以保证线程安全了。
2) 这样, 实例化代码只用执行一次, 后面再次访问时, 判断 if (singleton == null), 直接 return 实例化对象, 也避
免的反复进行方法同步.
3) 线程安全; 延迟加载; 效率较高
4) 结论: 在实际开发中, 推荐使用这种单例设计模式
7.静态内部类(推荐使用)
优缺点说明:
1) 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
2) 静态内部类方式在 Singleton 类被装载时并不会立即实例化, 而是在需要实例化时, 调用 getInstance 方法, 才
会装载 SingletonInstance 类, 从而完成 Singleton 的实例化。
3) 类的静态属性只会在第一次加载类的时候初始化, 所以在这里, JVM 帮助我们保证了线程的安全性, 在类进行
初始化时, 别的线程是无法进入的。
4) 优点: 避免了线程不安全, 利用静态内部类特点实现延迟加载, 效率高
5) 结论: 推荐使用.
8.枚举(推荐使用)
优缺点说明:
1) 这借助 JDK1.5 中添加的枚举来实现单例模式。 不仅能避免多线程同步问题, 而且还能防止反序列化重新创建
新的对象。
2) 这种方式是 Effective Java 作者 Josh Bloch 提倡的方式
3) 结论: 推荐使用
3.单例八种方式实现
package com.zrj.design.single;
/**
* 单例模式,双重检查
*
* @author zrj
* @date 2021/11/6
* @since V1.0
**/
public class Singleton {
/**
* 静态 volatile 修饰变量
* 保证内存可见性
* 禁止指令重排序
*/
private static volatile Singleton instance;
/**
* 私有构造方法防止修改
*/
private Singleton() {
}
/**
* 双重校验锁
*/
public static synchronized Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}