创建型模式之Singleton模式
单例模式大概是最直观的一种设计模式了,尽管直观却不简单。
数学与逻辑学中,singleton定义为“有且仅有一个元素的集合”,
单例模式可以如下定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供”。
我比较喜欢Design Patterns 一书中的描述"保证一个类仅有一个实例,并提供一个访问它的全局访问点"。
单例模式的特点
1.单例类只能有一个实例
2.单例类必须自己自己创建自己的唯一实例
3.例类必须给所有其他对象提供这一实例
单例模式的经典实现
实现单例,可以将类的构造方法限定为private,避免在外部实例化,然后在类中提供一个静态的实例并能够返回给使用者,
在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance方法访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class Singleton { /** * classic singleton * realize lazy loaded 添加判断uniqueInstance是否初始化,实现了使用时才进行加载 */ private static Singleton uniqueInstance= null ; private Singleton(){ //Exits only to defeat instantiation } public static Singleton getInstance(){ if (uniqueInstance== null ){ uniqueInstance = new Singleton(); } return uniqueInstance; } } |
线程安全的实现
经典的实现方法并没有考虑多线程的环境,试想存在两个线程A和B,
同时调用getInstance方法,线程A检查uniqueInstance是null,开始创建实例;
同时线程B检测到uniqueInstance是null,于是线程A/B各自创建了对象。
解决这个问题最简单的方法是加锁,为getInstance的静态方法添加synchronized关键字,
但是考虑到Synchronized同步锁的性能较低,可以调整Synchronized添加(加锁)的位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | public class SingletonSafed { /** * 要在于instance = new Singleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情。 * 给 instance 分配内存 * 调用 Singleton 的构造函数来初始化成员变量 * 将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了) * 使用volatile禁止指令重排序优化 */ //在Java里所有引用类型的静态以及实例成员,没有显式地初始化的,都会被设为null private volatile static SingletonSafed uniqueInstance; private SingletonSafed(){ //私有的构造方法,防止外部实例化 } //注意设置为静态方法 public static SingletonSafed getInstance(){ if (uniqueInstance== null ){ /** * 同步块加锁。双重检查锁,因为会有两次检查 instance == null,一次是在同步块外,一次是在同步块内。 * 为什么在同步块内还要再检验一次?因为可能会有多个线程一起进入同步块外的 if,如果在同步块内不进行二次检验的话就会生成多个实例。 */ synchronized (SingletonSafed. class ){ if (uniqueInstance== null ){ uniqueInstance= new SingletonSafed(); } } } return uniqueInstance; } } |
饿汉式 懒汉式和登记式
另外,一些文档会提到单例模式的三种形式(懒汉式,饿汉式,登记式),
其实饿汉式和懒汉式主要是线程安全的区别,同时懒汉式是延迟加载,
在需要的时候才创建对象,而饿汉式在虚拟机启动的时候就会创建,例如下面代码:
1 2 3 4 5 6 7 8 9 10 11 | //饿汉式单例类.在类初始化时,已经自行实例化 public class Singleton1 { //私有的默认构造子 private Singleton1() {} //已经自行实例化 private static final Singleton1 single = new Singleton1(); //静态工厂方法 public static Singleton1 getInstance() { return single; } } |
懒汉式单例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //懒汉式单例类.在第一次调用的时候实例化 public class Singleton2 { //私有的默认构造子 private Singleton2() {} //注意,这里没有final private static Singleton2 single= null ; //静态工厂方法 public synchronized static Singleton2 getInstance() { if (single == null ) { single = new Singleton2(); } return single; } } |
好文书签
聊聊并发(二)——Java SE1.6中的Synchronized
本文来自博客园,作者:邴越,转载请注明原文链接:https://www.cnblogs.com/binyue/p/3696565.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南