单例模式

  • 单例模式(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. [BOM]前端解析cookie为对象

    参考解析页面传参 var cookie_str = document.cookie var cookie_arr = cookie_str.split("; ") var cook ...

  2. C# 使用CefSharp完成自动签到

    1首先新建项目,右键项目选择管理NuGet 程序包,在浏览中搜索CefSharp.Winfrom ,点击安装,会自动安装相应的包.因为CefSharp是不能以AnyCPU的模式运行所以需要修改配置,有 ...

  3. Filament初探,全场景性能测试

    一直很想研究下Filament在移动端全场景(大约20万Triangle,约120个渲染实体)的实时帧率.终于在今天有时间腾出来研究下Filament在Android上的全场景PBR渲染性能. 这里以 ...

  4. pytorch学习笔记(3)--dataset使用

    下载数据集 import torchvision from torch.utils.tensorboard import SummaryWriter dataset_transform = torch ...

  5. git常用命令查询手册

    默认已经连接到远程仓库的情况下 本地文件夹初始化成git仓库.提交本地仓库并添加注释 git init git add 文件1(文件夹1) 文件2(文件夹2)... git commit -m &qu ...

  6. 算法与数据结构——AVL树(平衡二叉树)

    定义 typedef struct AVLNode* Position; typedef Position AVLTree; /* AVL树类型 */ struct AVLNode { Element ...

  7. C# 使用Enumerable.Range 打印数字

    static void Main(string[] args) { var list1 = Enumerable.Range(0, (int)Math.Pow(2, 22)).ToList(); va ...

  8. seleniumUI自动化学习记录

    2019.2.9 尝试了一个启动浏览器并打开指定网址的程序: 这里首先要注意的就是浏览器的版本和selenium jar包的版本必须符合才行,不然会报错 2019.9.16 必须要下载相应的chrom ...

  9. [api自动化]快速导出接口到jmeter脚本

    [场景]在项目做接口自动化的时候,大家一般找接口文档或者其他接口资料,逐一编写脚本.这样效率低,且容易由于文档未更新导致接口调试不通 [解决方案]页面上操作对应功能,直接捕获用到的接口,导出到jmet ...

  10. IDEA中maven项目右侧maven图标不见了

    右侧maven图标没有了 解决方法:双击shift(或者点击help-->Find Action) 打开搜索功能 ---> 搜素Maven Project--->选择Add Mave ...