Java设计模式【单例模式】
Java设计模式【单例模式】

单例模式
单例模式(Singleton Pattern)是一种创建型设计模式,其主要目的是确保一个类只有一个实例,并提供对该实例的唯一访问点。
优缺点
优点:
提供了对唯一实例的受控访问。
由于在系统内存中只存在一个对象,因此可以节约系统资源。
缺点:
单例类的扩展有很大的困难。
单例类的职责过重,在一定程度上违背了“单一职责原则”。
对象生命周期。 单例模式没有提出对象的销毁,在提供内存的管理的开发语言中,只有单例模式对象自己才能将对象实例销毁,因为只有它拥有对实例的引用。 在各种开发语言中,比如C++,其他类可以销毁对象实例,但是这么做将导致单例类内部的指针指向不明。
单例模式的使用
饿汉模式
- 静态成员变量
/**
* @author Physicx
* @date 2023/5/12 下午10:13
* @desc 单例
* Created with IntelliJ IDEA
*/
public class Singleton {
//初始化实例对象
private static final Singleton instance = new Singleton();
//私有化构造方法
private Singleton() {
}
//提供获取实例对象方法
public static Singleton getInstance() {
return instance;
}
}
- 静态代码块
/**
* @author Physicx
* @date 2023/5/12 下午10:13
* @desc 单例
* Created with IntelliJ IDEA
*/
public class Singleton {
//实例对象
private static final Singleton instance;
static {
instance = new Singleton();
}
//私有化构造方法
private Singleton() {
}
//提供获取实例对象方法
public static Singleton getInstance() {
return instance;
}
}
饿汉式单例的写法适用于单例对象较少的情况,这样写可以保证绝对的线程安全,执行效率比较高。但是缺点也很明显,饿汉式会在类加载的时候就将所有单例对象实例化,这样系统中如果有大量的饿汉式单例对象的存在,系统初始化的时候会造成大量的内存浪费,换句话说就是不管对象用不用,对象都已存在,占用内存。
懒汉模式
public class Singleton {
//实例对象
private static Singleton instance;
//私有化构造方法
private Singleton() {
}
//提供获取实例对象方法(线程安全)
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
线程安全的一种懒汉式写法,在类第一次使用的时候初始化,获取实例的静态方法由synchronized修饰,所以是线程安全的。这种方法每次获取实例对象都加锁同步,效率较低。
双重检测机制(DCL)
public class Singleton {
//实例对象
private static volatile Singleton instance;
//私有化构造方法
private Singleton() {
}
//提供获取实例对象方法
public static Singleton getInstance() {
if (instance == null) {
//加锁处理
synchronized (Singleton.class) {
if (instance==null) {
//初始化
instance = new Singleton();
}
}
}
return instance;
}
}
实例对象必须用 volatile 修饰,否则极端情况可能出现安全隐患。
以上初始化对象代码被编译后会变成以下三条指令:
分配对象的内存空间。
初始化对象。
设置instance指向刚才分配的内存空间。
如果按照上面的执行顺序则不加volatile没有问题,但是CPU或编译器为了提高效率,可能会进行指令重排,最终顺序变为:
分配对象的内存空间。
设置instance指向刚才分配的内存空间。
初始化对象。
当两个线程同时获取实例对象时,线程A已经将instance指向分配空间但未初始化对象,线程B此时第一次判空已不为空,于是返回instance实例,但是此时返回的实例未初始化会导致后续空指针异常。
DCL这种方式同样也是类第一次使用的时候初始化,初始化代码synchronized修饰线程安全,这种方式只会第一次实例对象才会进行同步,因此效率高。
《Java Concurrency in Practice》作者Brian Goetz在书中提到关于DCL的观点:促使DCL模式出现的驱动力(无竞争同步的执行速度很慢,以及JVM启动时很慢)已经不复存在,因而它不是一种高效的优化措施。延迟初始化占位类模式(静态内部类)能带来同样的优势,并且更容易理解。
静态内部类(延迟初始化)
public class Singleton {
//私有化构造方法
private Singleton(){}
//静态内部类(被调用时加载)
private static class SingletonHandle {
private static final Singleton instance = new Singleton();
}
//提供获取实例对象方法
public static Singleton getInstance() {
return SingletonHandle.instance;
}
}
利用静态内部类被调用时才加载的特性,通过静态初始化初始Singleton对象,由于JVM将在初始化期间获得一个锁,并且每个线程都至少获取一次这个锁以确保这个类已经加载,因此在静态初始化期间,内存写入操作将自动对所有线程可见。因此无论是在被构造期间还是被引用时,静态初始化的对象都不需要显式的同步。
线程安全,效率高,使用的时候才会初始化不浪费内存。
《Java Concurrency in Practice》作者Brian Goetz 推荐这种单例实现方式。
枚举实现方式
除了以上几种常见的实现方式之外,Google 首席 Java 架构师、《Effective Java》一书作者、Java集合框架的开创者Joshua Bloch在Effective Java一书中提到:单元素的枚举类型已经成为实现Singleton的最佳方法。
在这种实现方式中,既可以避免多线程同步问题;还可以防止通过反射和反序列化来重新创建新的对象。
public class Singleton {
//私有化构造方法
private Singleton() {}
enum SingletonEnum {
SINGLETON;
private final Singleton instance;
SingletonEnum() {
instance = new Singleton();
}
//提供获取实例对象方法
public Singleton getInstance() {
return instance;
}
}
}
调用方式如下:
public static void main(String[] args) {
Singleton instance1 = Singleton.SingletonEnum.SINGLETON.getInstance();
Singleton instance2 = Singleton.SingletonEnum.SINGLETON.getInstance();
System.out.println(instance2 == instance1);
}
普通的单例模式是可以通过反射和序列化/反序列化来破解的,jvm虚拟机会保证枚举类型不能被反射并且构造函数只被执行一次,而Enum由于自身的特性问题,是无法破解的。当然,由于这种情况基本不会出现,因此我们在使用单例模式的时候也比较少考虑这个问题。
总结
| 实现方式 | 优点 | 缺点 |
|---|---|---|
| 饿汉模式 | 线程安全,效率高 | 非懒加载 |
| 懒汉模式 | 线程安全,懒加载 | 效率低 |
| 双重检测机制 | 线程安全,懒加载,效率高 | |
| 静态内部类 | 线程安全,懒加载,效率高 | |
| 枚举 | 线程安全,效率高 | 非懒加载 |
由于单例模式的枚举实现代码比较简单,而且又可以利用枚举的特性来解决线程安全和单一实例的问题,还可以防止反射和反序列化对单例的破坏,因此在很多书和文章中都强烈推荐将该方法作为单例模式的最佳实现方法。
参考:单例模式详解(知乎文章)
设计模式相关其他文章:
Java设计模式总结
Java设计模式【单例模式】的更多相关文章
- java设计模式单例模式 ----懒汉式与饿汉式的区别
常用的五种单例模式实现方式 ——主要: 1.饿汉式(线程安全,调用率高,但是,不能延迟加载.) 2.懒汉式(线程安全,调用效率不高,可以延时加载.) ——其他: 1.双重检测锁式(由于JVM底层内部模 ...
- Java设计模式の单例模式
-------------------------------------------------- 目录 1.定义 2.常见的集中单例实现 a.饿汉式,线程安全 但效率比较低 b.单例模式的实现:饱 ...
- JAVA设计模式-单例模式(Singleton)线程安全与效率
一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...
- Java设计模式 - - 单例模式 装饰者模式
Java设计模式 单例模式 装饰者模式 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 静态代理模式:https://www.cnblogs.com/StanleyBlogs/p/1 ...
- 【设计模式】Java设计模式 - 单例模式
[设计模式]Java设计模式 - 单例模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 分享学习心得,欢迎指正,大家一起学习成长! 原创作品,更多关注我CSDN: ...
- Java 设计模式 —— 单例模式
1. 概念: 单例模式是一种常用的软件设计模式.核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源.如果 ...
- Java设计模式 - 单例模式 (懒汉方式和饿汉方式)
概念: Java中单例模式是一种常见的设计模式,单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 单例模式的写法有好几种,这 ...
- java设计模式——单例模式(一)
一. 定义与类型 定义:保证一个类仅有一个实例,并提供一个全局访问点 类型:创建型 二. 适用场景 想确保任何情况下都绝对只用一个实例 三. 优缺点 优点: 在内存里只有一个实例,减少了内存开销 可以 ...
- JAVA设计模式--单例模式
单例设计模式 Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点. 核心知识点如下: (1) 将采用单例 ...
- Java设计模式-单例模式(Singleton)
单例对象(Singleton)是一种常用的设计模式.在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在.这样的模式有几个好处: 1.某些类创建比较频繁,对于一些大型的对象,这是一笔 ...
随机推荐
- NX二次开发读属性/表达式封装函数
int Read_ATTR_Type(int ObjTag, char* Attr_Title); //读取属性返回属性类型 string Read_ATTR_StringValue(int ObjT ...
- JDK 7 HashMap 并发情况下的死锁问题
目录 问题描述 详细解释 问题描述 JDK7的 HashMap 解决冲突用的是链表,在插入链表的时候用的是头插法,每次在链表的头部插入新元素.resize() 的时候用的依然是头插,头插的话,如果某个 ...
- Android笔记--常用布局
线性布局--LinearLayout 线性布局的方向 orientation属性值:若为horizontal,内部视图在水平方向从左往右排列 若为vertical,内部视图在垂直方向从上往下排列 如果 ...
- 一起听、一起看、一起唱掀起Z世代青年社交浪潮
6月5日,声网Agora 联合人人都是产品经理在成都举办了主题为"社交泛娱乐APP运营增长力和新玩法解析"的沙龙.现场围绕社交泛娱乐新玩法解析以及出海的新机遇.支付痛点.增长.运营 ...
- 分布式搜索-elasticsearch
学习黑马- SpringCloud微服务技术栈项目的分布式搜索章节自行整理的笔记,方便日后的重构. 项目涉及技术 知识点是按照集数依次整理,方便日后回来查找. 考虑到不是固定的联网方式,时而WiFi, ...
- Java项目是不是分布式,真有那么重要吗?
大家好,我是3y啊. 大概不知道从什么时候,「微服务」「分布式」这两个词又再次频繁出现在我的视线里. 「微服务」「分布式」在我刚毕业的时候还是比较关注的,那时候还入门了一把SpringCloud,写了 ...
- AOP的九点核心概念和作用
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点 ...
- day10-SpringBoot的异常处理
SpringBoot异常处理 1.基本介绍 默认情况下,SpringBoot提供/error处理所有错误的映射,也就是说当出现错误时,SpringBoot底层会请求转发到/error这个映射路径所关联 ...
- 聊天小精灵ChatGPT,好与不好大揭秘!
一.引言 在一个遥远的地球上,有一个名为ChatGPT的魔法盒子,它能够用智慧回答你的问题,解决你的困扰.它是一个聪明的家伙,但和任何家伙一样,有优点也有缺点.现在就让我们一起来探索这个神秘的魔法盒子 ...
- 解决 ssh 找不到对应主机密钥类型
解决办法 如果最近升级到了 openssh 8.8 版,你会发现连接某些之前连接得好好的服务器突然无法连接: Unable to negotiate with x.x.x.x port 2222: n ...