Description: this article is talking about some knowledge of singleton
定义
确保一个类只有一个实例,并提供该实例的全局访问点。
好处: 有些实例,全局只需要一个就够了,使用单例模式就可以避免一个全局使用的类,频繁的创建和与销毁,耗费系统资源。
设计要素
- 一个私有构造函数(确保只能单例类自己创建实例)
- 一个私有静态变量(确保只有一个实例)
- 一个公有静态函数(给使用者提供调用方法)
单例模式的应用场景
- 网站计数器
- 应用程序的日志应用
- Web项目中的配置对象的读取
- 数据库连接池
- 多线程池
使用场景总结
频繁实例化然后又销毁的对象,使用单例模式可以提高性能
经常使用的对象,但实例化时耗费时间或者资源多,如数据库连接池,使用单例模式,可以提高性能,降低资源损坏
使用线程池之类的控制资源时,使用单例模式,可以方便资源之间的通信
单例模式六种实现方式
懒汉式(线程不安全)
1 | public Singleton{ |
说明
先不创建实例,当第一次被调用时,再创建实例
优点
延迟了了实例化,如果不需要使用该类,就不会被实例化,节约了系统资源
缺点
线程不安全,多线程环境下,如果多个线程同时进入了if(singleton == null) 若此时还未实例化,就会有多个线程执行实例化,就会实例化多个实例
饿汉式(线程安全)
1 | public class Singleton{ |
优点
提前实例化好一个实例,避免了线程不安全问题的实现
缺点
直接实例化好了实例,不再延迟实例化,若系统没有使用和这个实例,或者系统运行了很久之后才需要使用这个实例就会造成操作系统的资源浪费
懒汉式(线程安全)
1 | public class Singleton{ |
说明
在get的方法上加了一把锁,当多个线程访问时,每次只有拿到锁的线程能够进入该方法,避免了多线程不安全问题的出现
优点
延迟实例化,节约了资源,并且是线程安全的
缺点
性能降低了,因为即使实例已经实例化了,即后续不会再出现线程安全问题,但是锁还在,每次还是只能拿到锁的线程进入该方法,会使线程阻塞,等待时间过长。
双重检查锁实现(线程安全)
1 | public class Singleton{ |
volatile 关键字
singleton = new Singleton(); 这段代码执行时候分三步
- 为singleton分配内存空间
- 初始化singleton
- 将singleton指向分配的内存地址
正常的执行顺序是123,但是JVM的指令重排的特性,指令顺序有可能变成132,单线程时候指令重排没有什么问题,但是多线程环境中,会导致有些线程可能会获取到还没初始化的实例
volatile 会禁止jvm的指令重排
优点
延迟实例化,节约了资源,线程安全,并且相对于线程安全的懒汉式,性能提高了
缺点
volatile关键字对性能也有一些影响
静态内部类实现(线程安全)
1 | public class Singleton{ |
说明
首先,外部类singleton被加载时候,静态内部类SingletonHolder并没有被加载进内存,当调用getSingleton方法时候会运行return语句,触发了SingletonHolder.INSTANCE, 此时静态内部类才会被加载进内存,并且初始化实例
优点
延迟实例化,节约了资源,并且线程安全,性能也提高了
枚举类实现(线程安全)
1 | public enum Singleton{ |
说明
默认枚举类型的创建就是线程安全的,且在任何情况下都是单例
优点
写法简单,线程安全,天然防止反射和反序列化调用
防止反序列化
- 序列化: 把java对象转换为字节序列的过程
- 反序列化:通过这些字节序列在内存中新建java对象的过程
- 说明: 反序列化讲一个单例实例对象写到磁盘再度回来,从而获得了一个新的实例
我们要防止反序列化,避免得到多个实例
枚举类天然防止反序列化