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设计模式——单例模式(一)
一. 定义与类型 定义:保证一个类仅有一个实例,并提供一个全局访问点 类型:创建型 二. 适用场景 想确保任何情况下都绝对只用一个实例 三. 优缺点 优点: 在内存里只有一个实例,减少了内存开销 可以 ...
随机推荐
- python菜鸟学习: 5.字符串的基本用法,进度条显示
# -*- coding: utf-8 -*-import sys,timestr1 = "my name is liyuzhoupan"# 首字母大写print(str1.cap ...
- 11.20 dom 浏览器对象模型
1.window.open(url,ways) url 是打开的网页地址 ways 打开的方式 _self 2.window.close() 3.浏览器用户信息:Window.navigator 4. ...
- ANSYS Electronics Suite 19.2下载地址及其安装教程
ANSYS Electronics Suite 19.2下载安装教程 1.下载地址https://getintopc.site/ansys-electronics-suite-19-2-free-do ...
- oracle修改表中的列
declare v_Count1 int := 0; v_Count2 int := 0; v_Count3 int := 0; v_Count4 int := 0; v_Count5 int := ...
- 20220720 第七组 陈美娜 Java String用法
关于String引用数据类型 1.字符串中,两个变量的==指的是虚地址 2.String一旦声明不可改变:赋值进去,原值不会被替代.原值也可能指向其他地址: 3.s.length():字符的个数 4. ...
- pwm 理解
PWM: 假设PWM的时钟主频是 PWM_CLK_FREQ Hz,则如果需要输出频率为 xHz,占空比为 y% 的波形时, 则只需要在定时器的周期寄存器中写入(PWM_CLK_FREQ / x),在 ...
- Linux系统管理实战-配置静态IP
配置静态IP 前置条件 防火墙: EL7 EL6 查看状态: # systemctl status firewalld # /etc/init.d/iptables status 立即关闭: # sy ...
- Java中接口相关知识
1.接口 1.1接口概述 接口就是一种公共的规范标准,只要符合标准,大家都可以通用 Java中的接口更多的体现在对行为的抽象 1.2接口的特点 接口用关键字interface修饰,格式:public ...
- TCP 异常断开连接的过程
Tcp连接正常断开的情况,大家都了解,也就是一端发送Fin报文开启四次挥手,然后 sock 结构销毁,但是我之前很少去追踪 Tcp 在对端宕机,进程 Crash 之后的行为逻辑.前段时间正巧遇到了这样 ...
- 01.数据库基础、JDBC
一.数据库 数据库:用于存储和管理数据的仓库. 数据库的特点 持久化储存数据,数据库就是一个文件系统. 方便储存和管理数据. 使用了统一的方式操作数据库--SQL. 配置 Mysql 服务启动 手动 ...