单例模式

  • 单例模式(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设计模式——单例模式的更多相关文章

  1. 设计模式--单例模式(Singleton pattern)及应用

    单例模式 参考文档: 该文仅介绍spring的单例模式:spring 的单例模式 介绍原理:Spring的单例模式底层实现 参考书籍:漫谈设计模式:从面向对象开始-刘济华.pdf 1. 单例模式解析 ...

  2. Spring 5 , 狭义上的spring 以及 广义上的spring , spring 设计模式

    Spring 5 距离 Spring4 发布有4年多, 所以说Spring 5是一个重要的版本 要构建和运行Spring 5应用程序, 至少需要Java EE 7 和JDK 8 , 之前的JDK和Ja ...

  3. 设计模式 单例模式(Singleton) [ 转载2 ]

    设计模式 单例模式(Singleton) [ 转载2 ] @author java_my_life 单例模式的结构 单例模式的特点: 单例类只能有一个实例. 单例类必须自己创建自己的唯一实例. 单例类 ...

  4. 设计模式 单例模式(Singleton) [ 转载 ]

    设计模式 单例模式(Singleton) [ 转载 ] 转载请注明出处:http://cantellow.iteye.com/blog/838473 前言 懒汉:调用时才创建对象 饿汉:类初始化时就创 ...

  5. c#设计模式-单例模式(面试题)

    c#设计模式-单例模式 单例模式三种写法: 第一种最简单,但没有考虑线程安全,在多线程时可能会出问题, public class Singleton { private static Singleto ...

  6. java设计模式单例模式 ----懒汉式与饿汉式的区别

    常用的五种单例模式实现方式 ——主要: 1.饿汉式(线程安全,调用率高,但是,不能延迟加载.) 2.懒汉式(线程安全,调用效率不高,可以延时加载.) ——其他: 1.双重检测锁式(由于JVM底层内部模 ...

  7. 最简单的设计模式——单例模式的演进和推荐写法(Java 版)

    前言 如下是之前总结的 C++ 版的:软件开发常用设计模式—单例模式总结(c++版),对比发现 Java 实现的单例模式和 C++ 的在线程安全上还是有些区别的. 概念不多说,没意思,我自己总结就是: ...

  8. ES6教程-字符串,函数的参数,了解函数的arguments对象,js面向对象,设计模式-单例模式,解构赋值

    前言 主要讲解了ES6对字符串的拓展,包括includes,startsWith和endsWith,另外增加了字符串模板. Start includes()是否包含 startsWith()以什么开头 ...

  9. Java设计模式の单例模式

    -------------------------------------------------- 目录 1.定义 2.常见的集中单例实现 a.饿汉式,线程安全 但效率比较低 b.单例模式的实现:饱 ...

  10. java设计模式——单例模式(一)

    一. 定义与类型 定义:保证一个类仅有一个实例,并提供一个全局访问点 类型:创建型 二. 适用场景 想确保任何情况下都绝对只用一个实例 三. 优缺点 优点: 在内存里只有一个实例,减少了内存开销 可以 ...

随机推荐

  1. 2022-04-19内部群每日三题-清辉PMP

    1.一个项目的完工预算(BAC)为100,000美元.目前的项目报告显示该项目符合进度和预算.一位团队成员对相关工作包进行自下而上估算,表明剩余工作将比原计划少用10,000美元,并且仍能按进度计划交 ...

  2. 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 ...

  3. HDFS 机架感知与副本放置策略

    HDFS 机架感知与副本放置策略 机架感知(RackAwareness) 通常,大型 Hadoop 集群会分布在很多机架上,在这种情况下, 希望不同节点之间的通信能够尽量发生在同一个机架之内,而不是跨 ...

  4. 1903021126 申文骏 Java 第七周作业 客户类测试

     项目  内容 课程班级博客链接 19级信计班(本) 作业要求链接 Java 第七周作业 博客名称 1903021126  申文骏  Java 第七周作业  客户类测试 要求 每道题要有题目,代码(使 ...

  5. 进入容器后不显示id

    https://www.656463.com/wenda/dockerexejrrqbxsrqID_493 net=host的原因

  6. 使用git钩子防止合并分支

    git是一款实用的版本管理工具,我们通过git init初始化一个git仓库,git会在当前目录为我们生成一个.git/目录,用来管理我们的版本文件信息. 在这个目录中有一个二级目录.git/hook ...

  7. Topsis法的python实现

    TOPSIS (Technique for Order Preference by Similarity to an Ideal Solution )法是C.L.Hwang和K.Yoon于1981年首 ...

  8. 查看Linux 日志

    # 直接定位到第100行 less +100g xx.log   # 定位到最后一行 less +GG xx.log   # 定位到第100个字节的位置 less +100P xx.log   # 直 ...

  9. PVE设置定时关闭、启动虚拟机

    shell中输入命令: crontab -e 进入对应cron 添加命令: 例如: 00 2 * * * pvesh create /nodes/pve/qemu/102/status/stop 00 ...

  10. JS变量之间赋值,修改变量值,原变量会随之改变的问题

    现象: 开发vue项目的过程中,需要多次用到一份基础数据,为减少代码量,提高一下复用效果,便用变量A来定义,在项目中需要用到时就用变量A进行赋值. 在项目中调用时,我新定义一个变量B,再将变量A赋值给 ...