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();
}
}
总结
单例模式可以保证内存里只有一个实例,减少了内存的开销,还可以避免对资源的多重占用。
Spring设计模式——单例模式的更多相关文章
- 设计模式--单例模式(Singleton pattern)及应用
单例模式 参考文档: 该文仅介绍spring的单例模式:spring 的单例模式 介绍原理:Spring的单例模式底层实现 参考书籍:漫谈设计模式:从面向对象开始-刘济华.pdf 1. 单例模式解析 ...
- Spring 5 , 狭义上的spring 以及 广义上的spring , spring 设计模式
Spring 5 距离 Spring4 发布有4年多, 所以说Spring 5是一个重要的版本 要构建和运行Spring 5应用程序, 至少需要Java EE 7 和JDK 8 , 之前的JDK和Ja ...
- 设计模式 单例模式(Singleton) [ 转载2 ]
设计模式 单例模式(Singleton) [ 转载2 ] @author java_my_life 单例模式的结构 单例模式的特点: 单例类只能有一个实例. 单例类必须自己创建自己的唯一实例. 单例类 ...
- 设计模式 单例模式(Singleton) [ 转载 ]
设计模式 单例模式(Singleton) [ 转载 ] 转载请注明出处:http://cantellow.iteye.com/blog/838473 前言 懒汉:调用时才创建对象 饿汉:类初始化时就创 ...
- c#设计模式-单例模式(面试题)
c#设计模式-单例模式 单例模式三种写法: 第一种最简单,但没有考虑线程安全,在多线程时可能会出问题, public class Singleton { private static Singleto ...
- java设计模式单例模式 ----懒汉式与饿汉式的区别
常用的五种单例模式实现方式 ——主要: 1.饿汉式(线程安全,调用率高,但是,不能延迟加载.) 2.懒汉式(线程安全,调用效率不高,可以延时加载.) ——其他: 1.双重检测锁式(由于JVM底层内部模 ...
- 最简单的设计模式——单例模式的演进和推荐写法(Java 版)
前言 如下是之前总结的 C++ 版的:软件开发常用设计模式—单例模式总结(c++版),对比发现 Java 实现的单例模式和 C++ 的在线程安全上还是有些区别的. 概念不多说,没意思,我自己总结就是: ...
- ES6教程-字符串,函数的参数,了解函数的arguments对象,js面向对象,设计模式-单例模式,解构赋值
前言 主要讲解了ES6对字符串的拓展,包括includes,startsWith和endsWith,另外增加了字符串模板. Start includes()是否包含 startsWith()以什么开头 ...
- Java设计模式の单例模式
-------------------------------------------------- 目录 1.定义 2.常见的集中单例实现 a.饿汉式,线程安全 但效率比较低 b.单例模式的实现:饱 ...
- java设计模式——单例模式(一)
一. 定义与类型 定义:保证一个类仅有一个实例,并提供一个全局访问点 类型:创建型 二. 适用场景 想确保任何情况下都绝对只用一个实例 三. 优缺点 优点: 在内存里只有一个实例,减少了内存开销 可以 ...
随机推荐
- 2022-04-19内部群每日三题-清辉PMP
1.一个项目的完工预算(BAC)为100,000美元.目前的项目报告显示该项目符合进度和预算.一位团队成员对相关工作包进行自下而上估算,表明剩余工作将比原计划少用10,000美元,并且仍能按进度计划交 ...
- Nebula Exchange与集群guava版本不一致问题
1.集群版本 Hadoop 3.1.1.3.1.4.0-315 Hive (version 3.1.0.3.1.4.0-315) Spark 2.3.2.3.1.4.0-315 Scala versi ...
- HDFS 机架感知与副本放置策略
HDFS 机架感知与副本放置策略 机架感知(RackAwareness) 通常,大型 Hadoop 集群会分布在很多机架上,在这种情况下, 希望不同节点之间的通信能够尽量发生在同一个机架之内,而不是跨 ...
- 1903021126 申文骏 Java 第七周作业 客户类测试
项目 内容 课程班级博客链接 19级信计班(本) 作业要求链接 Java 第七周作业 博客名称 1903021126 申文骏 Java 第七周作业 客户类测试 要求 每道题要有题目,代码(使 ...
- 进入容器后不显示id
https://www.656463.com/wenda/dockerexejrrqbxsrqID_493 net=host的原因
- 使用git钩子防止合并分支
git是一款实用的版本管理工具,我们通过git init初始化一个git仓库,git会在当前目录为我们生成一个.git/目录,用来管理我们的版本文件信息. 在这个目录中有一个二级目录.git/hook ...
- Topsis法的python实现
TOPSIS (Technique for Order Preference by Similarity to an Ideal Solution )法是C.L.Hwang和K.Yoon于1981年首 ...
- 查看Linux 日志
# 直接定位到第100行 less +100g xx.log # 定位到最后一行 less +GG xx.log # 定位到第100个字节的位置 less +100P xx.log # 直 ...
- PVE设置定时关闭、启动虚拟机
shell中输入命令: crontab -e 进入对应cron 添加命令: 例如: 00 2 * * * pvesh create /nodes/pve/qemu/102/status/stop 00 ...
- JS变量之间赋值,修改变量值,原变量会随之改变的问题
现象: 开发vue项目的过程中,需要多次用到一份基础数据,为减少代码量,提高一下复用效果,便用变量A来定义,在项目中需要用到时就用变量A进行赋值. 在项目中调用时,我新定义一个变量B,再将变量A赋值给 ...