Spring设计模式——单例模式
单例模式
- 单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。
- 单例模式是创建型模式。
饿汉单例模式
- 饿汉单例模式在类的加载时候就立即初始化,并且创建对象。
- 它绝对线程安全,在线程还没出现以前就实例化了,不可能存在访问安全问题
优点:没有加任何锁、执行效率比较高,用户体验比懒汉单例模式更好
缺点:类加载的时候就初始化,不管用与不用都占着空间,浪费了内存,有可能“占着茅坑不拉屎”
package org.example.spring.designpattern.singleton; /** * TODO 饿汉单例模式 * * @author ss_419 * @version 1.0 * @date 2023/3/9 08:16 */ public class HungrySingleton { //先静态、后动态 //先属性、后方法 //先上后下 /** * 全局访问点,线程先没出现以前就实例化,不管用不用都占用空间 */ private static final HungrySingleton INSTANCE = new HungrySingleton(); private HungrySingleton() { } // 提供一个全局访问点 public static HungrySingleton getInstance() { return INSTANCE; } }
还有另一种写法,利用静态代码块的机制:
package org.example.spring.designpattern.singleton; /** * TODO 饿汉单例模式——静态代码块 * * @author ss_419 * @version 1.0 * @date 2023/3/9 08:19 */ public class HungryStaticSingleton { /** * 提供一个全局访问点 */ private static final HungryStaticSingleton INSTANCE ; static { INSTANCE = new HungryStaticSingleton(); } private HungryStaticSingleton() { } public static HungryStaticSingleton getInstance() { return INSTANCE; } }
饿汉单例模式适合用于单例对象比较少的情况
懒汉单例模式
懒汉单例模式,顾名思义它......比较懒,所以它的特点是:被外部类调用的时候内部类才会加载。
package org.example.spring.designpattern.singleton; /** * TODO 懒汉单例模式 * * @author ss_419 * @version 1.0 * @date 2023/3/9 08:23 */ public class LazySimpleSingleton { private LazySimpleSingleton() { } //静态块,公共内存区域 private static LazySimpleSingleton lazy = null; /** * 静态方法 * 实例不存在的时候new一个 * @return */ public static LazySimpleSingleton getInstance() { if (lazy == null) { lazy = new LazySimpleSingleton(); } return lazy; } /** * 创建一个线程 */ static class testSingletonThread implements Runnable{ @Override public void run() { LazySimpleSingleton singleton = LazySimpleSingleton.getInstance(); System.out.println(Thread.currentThread().getName() + ":" + singleton); } } /** * 测试 * @param args */ public static void main(String[] args) { Thread t1 = new Thread(new testSingletonThread()); Thread t2 = new Thread(new testSingletonThread()); t1.start(); t2.start(); System.out.println("End"); } }
运行结果如下:
以上执行结果有概率出现两种不同结果,存在很大的线程安全隐患;这里教大家一种新技能,用线程模式调试,手动控制线程的执行顺序来跟踪内存的变化。
线程模式调试
先给testSingletonThread打上断点,如下图所示:
使用鼠标右键单击断点,切换为Thread模式,如下图所示:
然后给LazySimpleton类打上断点,同样标记为Thread模式,如下图所示:
转到客户端测试代码,同样也打上断点,同时改为Thread模式,如下图所示:
开始Debug之后,会看到Debug控制台可以自由切换Thread的运行状态:
通过不断切换线程,并观测其内存状态,我们发现在线程环境下LazySimpleSingleton被实例化了两次。有时候我们得到的运行结果可能是相同的两个对象,实际上是被后面执行的线程覆盖了,我们看到了一个假象,线程安全隐患依旧存在。
那么,我们如何来优化代码,使得懒汉单例模式在线程环境下安全呢?看下面的代码,给getInstance()加上synchronized关键字,使这个方法变成线程同步方法:
双重检查锁懒汉单例模式:
package org.example.spring.designpattern.singleton; /** * TODO 双重检查锁懒汉单例模式 * * @author ss_419 * @version 1.0 * @date 2023/3/9 08:48 */ public class LazyDoubleCheckSingleton { //静态块,公共内存区域 private static LazyDoubleCheckSingleton lazy = null; private LazyDoubleCheckSingleton() { } public static LazyDoubleCheckSingleton getInstance() { if (lazy == null) { synchronized (LazyDoubleCheckSingleton.class){ if (lazy == null) { lazy = new LazyDoubleCheckSingleton(); // 1、分配内存给这个对象 // 2、初始化对象 // 3、设置lazy指向刚分配的内存地址 } } } return lazy; } }
最终版本,采用静态内部类的方式:
package org.example.spring.designpattern.singleton; /** * TODO 采用静态内部类的懒汉单例模式 * * @author ss_419 * @version 1.0 * @date 2023/3/9 08:52 */ public class LazyInnerClassSingleton { // 使用LazyInnerClassGeneral的时候,默认会先初始化内部类 // 如果没使用,则内部类是不加载的 private LazyInnerClassSingleton() { } // 每一个关键字都不是多余的,static是为了使单例的空间共享,保证这个方法不会被重写、重载 public static final LazyInnerClassSingleton getInstance() { // 在返回结果之前,一定会先加载内部类 return LazyHolder.INSTANCE; } // 默认不加载 private static class LazyHolder{ private static final LazyInnerClassSingleton INSTANCE = new LazyInnerClassSingleton(); } }
史上最牛的单例模式实现方式【防止反射破坏掉单例】
package org.example.spring.designpattern.singleton; /** * TODO 自认为史上最强的单例模式 * * @author ss_419 * @version 1.0 * @date 2023/3/9 08:58 */ public class LazyInnerClassSingletonPlus { // 使用LazyInnerClassGeneral的时候,默认会先初始化内部类 // 如果没使用,则内部类是不加载的 private LazyInnerClassSingletonPlus() { if (LazyHolder.INSTANCE != null){ throw new RuntimeException("不允许创建多个实例"); } } // 每一个关键字都不是多余的,static是为了单例的空间共享,保证这个方法不会被重写、重载 public static final LazyInnerClassSingletonPlus getInstance(){ return LazyHolder.INSTANCE; } // 默认不加载 private static class LazyHolder{ private static final LazyInnerClassSingletonPlus INSTANCE = new LazyInnerClassSingletonPlus(); } }
线程单例实现ThreadLocal
ThreadLocal不能保证其创建的对象是全局唯一的,但是能保证在单个线程中是唯一的,天生是线程安全的:
package org.example.spring.designpattern.singleton; /** * TODO 线程单例实现ThreadLocal * * @author ss_419 * @version 1.0 * @date 2023/3/9 09:05 */ public class ThreadLocalSingleton { private static final ThreadLocal<ThreadLocalSingleton> threadLocalSingleton = new ThreadLocal<ThreadLocalSingleton>(){ @Override protected ThreadLocalSingleton initialValue() { return new ThreadLocalSingleton(); } }; private ThreadLocalSingleton() { } public static ThreadLocalSingleton getInstance() { return threadLocalSingleton.get(); } }
总结
单例模式可以保证内存里只有一个实例,减少了内存的开销,还可以避免对资源的多重占用。
posted on 2023-03-09 09:11 JavaCoderPan 阅读(174) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南