什么是单例模式?
顾名思义,就是只有一个实例,也就是说一个应用程序中只有一个实例对象.
既然一个系统,一个应用程序中只有一个实例,那么也就说明了如果操作这一个对象,必然涉及到共享资源,涉及到资源分配问题,资源竞争等问题.
那么我们的应用场景是什么呢?
1. 网站的在线人数. 网站的在线人数在某一个时刻,所有人看到的是一样的, 是这个网站这个时刻,所有用户所共享的.
2. 池化技术. 比如数据库的连接池. 每个数据库的可支持的连接数量是有限,而且连接对象创建和销毁也是比较耗内存的. 通过一个统一的入口去控制,可以保证对数据库的压力在可控的范围内,同是也可以保证出具库连接的持续使用.
3. 配置中心. 一个应用程序针对通过一个配置文件的加载只需要加载一次即可,不需要多次加载.
其实以上只是一些常见的应用场景,当然单例模式的应用场景也远不止于此.
本文参考的博客地址为:https://www.cnblogs.com/mr-yang-localhost/p/9644417.html
参考的博客的写的很不错, 如果想了解更多,可以去看看这篇参考的博客
接下来我们来看一下常见的实现方式,以及其中的对比.
饿汉式,即直接初始化好,使用的时候直接调用即可.
package com.cbl.design.singletondesign; public class HungrySingleton { private static final HungrySingleton singleton = new HungrySingleton(); //限制外部产生HungrySingleton对象 private HungrySingleton(){ } //向外提供获取示例的静态方法 public static HungrySingleton getInstance() { return singleton; } }
懒汉式, 顾名思义,就是懒,需要的时候再去初始化
package com.cbl.design.singletondesign; public class LazySingleton { private static volatile LazySingleton singleton = null; private LazySingleton() { } public static LazySingleton getSingleton() { //不用每次获取对象都强制加锁,为了提升了效率 if (singleton == null) { synchronized (LazySingleton.class) { if (singleton == null) { singleton = new LazySingleton(); } } } return singleton; } }
静态内部类的方式
package com.cbl.design.singletondesign; public class StaticInnerSingleton { /** * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 * 没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。 */ private static class Singleton2Holder { /** * 静态初始化器,由JVM来保证线程安全 */ private static StaticInnerSingleton singleton = new StaticInnerSingleton(); } private StaticInnerSingleton() { //System.out.println("singleton2 private construct method called"); } public static StaticInnerSingleton getSingleton() { //System.out.println("singleton2 getSingleton method called"); return Singleton2Holder.singleton; } private String name; public void desc() { //System.out.println("singleton2 desc method called"); } }
还有一种实现方式是依赖于枚举, 我们知道枚举中的对象是实例化好的,而且枚举天生要求枚举类的构造器必须私有,而且本身还是被final 修饰的, 不可被继承
package com.cbl.design.singletondesign; public enum EnumSingleton { INSTANCE; public String getDesc() { return "desc"; } public static void process() { System.out.println("static process method"); } private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
以上是4中常见的构造单例模式的四种线程安全的构造单例的设计模式.网上可能还有其余的非线程安全的构建单例的方法, 但是线程安全的主要就是这四种.
(1) : 饿汉式 : 加载类的时候就会进行实例化,,如果后续没有用到,则会搞成资源浪费.
(2) : 懒汉式 : 使用的时候再去初始化,这样在前几次调用并发调用的时候,会出现资源竞争, 等待时间较久,但是后续再次调用的时候, 由于是双重判断加加锁校验机制, 不会出现阻塞.
(3): 静态内部类 : 也是在使用的时候再去加载内部类并初始化外部类的对象,第一次调用会比较慢.
(4):枚举单例: 类加载的时候就穿创建好对象了, 特点和饿汉式很像.
上面是这几种单例方式的特点,其中懒汉式,饿汉式,静态内部类的方式都不能防止反射,只有枚举类可以防止反射.
但是一般我们的应用程序只需要保证可以正常实现单例即可,没必要过多的去防止单例模式,我们使用单例模式,就是想构造单例对象的,但是我们自己又去使用反射去破坏单例模式,这不是自己给自己找麻烦吗.