设计模式 五种不同的单例模式 懒汉式 饿汉式 枚举单例 容器化单例(Spring单例源码分析) 线程单例

单例模式

第一种 饿汉式

优点:执行效率高,性能高,没有任何的锁

缺点:某些情况下,可能会造成内存浪费

/**
 * @author LionLi
 */
public class HungrySingleton {

    private static final HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton(){}

    public static HungrySingleton getInstance(){
        return  hungrySingleton;
    }
}

测试用例与结果

/**
 * @author LionLi
 */
public class Test {
    public static void main(String[] args){
        ExecutorService executor = Executors.newFixedThreadPool(5);
        PrintStream out = System.out;
        executor.execute(() -> out.println(HungrySingleton.getInstance()));
        executor.execute(() -> out.println(HungrySingleton.getInstance()));
        executor.execute(() -> out.println(HungrySingleton.getInstance()));
        executor.execute(() -> out.println(HungrySingleton.getInstance()));
        executor.execute(() -> out.println(HungrySingleton.getInstance()));
        executor.shutdown();
    }
}


多次运行符合要求不会出现问题

第二种 懒汉式

优点:节省了内存,线程安全
缺点:性能低

测试用例以下通用

/**
 * @author LionLi
 */
public class Test {
    public static void main(String[] args){
        ExecutorService executor = Executors.newFixedThreadPool(5);
        PrintStream out = System.out;
        executor.execute(() -> out.println(LazySingletion.getInstance()));
        executor.execute(() -> out.println(LazySingletion.getInstance()));
        executor.execute(() -> out.println(LazySingletion.getInstance()));
        executor.execute(() -> out.println(LazySingletion.getInstance()));
        executor.execute(() -> out.println(LazySingletion.getInstance()));
        executor.shutdown();
    }
}

第一版本

/**
 * @author LionLi
 */
public class LazySingletion {
    private static LazySingletion instance;
    private LazySingletion(){}

    public static LazySingletion getInstance(){
        if(instance == null){
            instance = new LazySingletion();
        }
        return instance;
    }
}

测试失败无法保证单例

第二版本 增加 synchronized 锁

/**
 * @author LionLi
 */
public class LazySingletion {
    private static LazySingletion instance;
    private LazySingletion(){}

    public synchronized static LazySingletion getInstance(){
        if(instance == null){
            instance = new LazySingletion();
        }
        return instance;
    }
}


测试成功 可以保证单例 但性能较低 所有的线程全都被阻塞到方法外部排队处理

第三版本 细化锁 只锁创建方法提高性能

优点: 性能高了,线程安全了

缺点:可读性难度加大,不够优雅

此方法在各大开源框架源码内最为常见 又名双重校验单例

/**
 * @author LionLi
 */
public class LazySingleton {
    /**
     * volatile 保证原子性具体用法百度
     */
    private volatile static LazySingleton instance;
    private LazySingleton(){}

    public static LazySingleton getInstance(){
        // 检查实例是否已经初始化 如果已经初始化直接返回 避免进入锁提高性能
        if (instance == null) {
            synchronized (LazySingleton.class) {
                // 重新检查是否已经被其他线程初始化
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}



测试成功 可以保证单例 性能还高 可以避免不必要的加锁

第三种 枚举单例

在这种实现方式中,既可以避免多线程同步问题,还可以防止通过反射和反序列化来重新创建新的对象。

Java虚拟机会保证枚举对象的唯一性,因此每一个枚举类型和定义的枚举变量在JVM中都是唯一的。

/**
 * @author LionLi
 */
public enum EnumSingleton {
    INSTANCE;
    private Object data;
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
}

测试代码与结果

/**
 * @author LionLi
 */
public class Test {
    public static void main(String[] args){
        ExecutorService executor = Executors.newFixedThreadPool(5);
        PrintStream out = System.out;
        // 设置一个对象便于查看内存地址
        EnumSingleton.INSTANCE.setData(new Object());
        executor.execute(() -> out.println(EnumSingleton.INSTANCE.getData()));
        executor.execute(() -> out.println(EnumSingleton.INSTANCE.getData()));
        executor.execute(() -> out.println(EnumSingleton.INSTANCE.getData()));
        executor.execute(() -> out.println(EnumSingleton.INSTANCE.getData()));
        executor.execute(() -> out.println(EnumSingleton.INSTANCE.getData()));
        executor.shutdown();
    }
}



测试成功 可以保证单例 代码简单非常优雅

第四种 Spring中的单例模式实现 也可以称为 容器化单例

大家可以通过idea搜索找到 Spring 源码中的 DefaultSingletonBeanRegistrygetSingleton 方法 查看 Spring 是如何编写的

这里涉及到三个单例容器:

  • singletonObjects
  • earlySingletonObjects
  • singletonFactories

单例的获取顺序是singletonObjects -> earlySingletonObjects -> singletonFactories 这样的三级缓存

我们发现,在 singletonObjects 中获取 bean的时候,没有使用 synchronized 关键字

而在 singletonFactoriesearlySingletonObjects 中的操作都是在 synchronized 代码块中完成的,正好和他们各自的数据类型对应

singletonObjects 使用的使用 ConcurrentHashMap 线程安全,而 singletonFactoriesearlySingletonObjects 使用的是 HashMap 线程不安全。

从字面意思来说:singletonObjects 指单例对象的缓存,singletonFactories 指单例对象工厂的缓存,earlySingletonObjects 指提前曝光的单例对象的缓存。

以上三个构成了三级缓存,Spring 就用这三级缓存巧妙的解决了循环依赖问题。

除了这三个缓存之外 最核心的就是上面讲到的 双重校验单例 写法

第五种 特殊单例 线程单例

顾名思义 保证在所有线程内的单例

常见使用场景 日志框架 确保每个线程内都有一个单例日志实例 保证日志记录和输出的唯一性

提到线程 我们肯定会想到 在线程内最常使用的东西 那就是 TheadLocal 他可以保证线程之间的变量隔离 我们就基于他来实现线程单例

public class ThreadLocalSingleton {
    // 通过 ThreadLocal 的初始化方法 withInitial 初始化对象实例 保证线程唯一
    private static final ThreadLocal<ThreadLocalSingleton> threadLocaLInstance =
            ThreadLocal.withInitial(() -> new ThreadLocalSingleton());

    private ThreadLocalSingleton(){}

    public static ThreadLocalSingleton getInstance(){
        return threadLocaLInstance.get();
    }
}

测试用例与运行结果

/**
 * @author LionLi
 */
public class Test {
    public static void main(String[] args){
        ExecutorService executor = Executors.newFixedThreadPool(5);
        PrintStream out = System.out;
        executor.execute(() -> {
            out.println(ThreadLocalSingleton.getInstance());
            out.println(ThreadLocalSingleton.getInstance());
        });
        executor.execute(() -> {
            out.println(ThreadLocalSingleton.getInstance());
            out.println(ThreadLocalSingleton.getInstance());
        });
        executor.shutdown();
    }
}

测试符合预期 不同线程下的实例是单例的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/253633.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

05 动态渲染数据

概述 实际上动态渲染数据&#xff0c;在《使用CDN开发Vue3项目》中就已经学习过了&#xff0c;核心代码如下&#xff1a; <div id"vue-app">{{text}}</div> <script src"https://cdn.staticfile.org/vue/3.0.5/vue.global.js"></sc…

设计模式详解---单例模式

1. 设计模式详解 单例模式是一种创建对象的设计模式&#xff0c;它确保一个类只有一个实例&#xff0c;并提供全局访问点以获取该实例。 在单例模式中&#xff0c;类负责创建自己的唯一实例&#xff0c;并确保任何其他对象只能访问该实例。这对于需要共享状态或资源的情况非常有…

[楚慧杯 2023] web

文章目录 eaaevalupload_shell eaaeval 打开题目&#xff0c;源码给了用户密码 登陆后啥也没有&#xff0c;扫一下发现源码泄露www.zip <?php class Flag{public $a;public $b;public function __construct(){$this->a admin;$this->b admin;}public function _…

医疗智能化革命:AI技术引领医疗领域的创新进程

一、“AI”医疗的崛起 随着人工智能&#xff08;AI&#xff09;技术的崛起&#xff0c;"AI"医疗正在以惊人的速度改变着医疗行业的面貌。AI作为一种强大的工具&#xff0c;正在为医疗领域带来前所未有的创新和突破。它不仅在医学影像诊断、病理学分析和基因组学研究等…

京东体育用品销售数据分析与可视化系统

京东体育用品销售数据分析与可视化系统 前言数据爬取模块1. 数据爬取2. 数据处理3. 数据存储 数据可视化模块1. 数据查看2. 店铺商品数量排行3. 整体好评率4. 不同品牌市场占比5. 品牌差评率排名6. 品牌价格排名7. 品牌评论数量分布 创新点 前言 在体育用品行业&#xff0c;了…

ElasticSearch学习篇8_Lucene之数据存储(Stored Field、DocValue、BKD Tree)

前言 Lucene全文检索主要分为索引、搜索两个过程&#xff0c;对于索引过程就是将文档磁盘存储然后按照指定格式构建索引文件&#xff0c;其中涉及数据存储一些压缩、数据结构设计还是很巧妙的&#xff0c;下面主要记录学习过程中的StoredField、DocValue以及磁盘BKD Tree的一些…

实现el-table操作列点击弹出echarts

代码&#xff1a; <el-table-column :width"90"><template #default"scope"><el-popover placement"left-end" width"550" trigger"click"><div><div style"font-size: 18px; margin-left…

Harmony4.0鸿蒙应用开发初识+实践小案例

Harmony4.0鸿蒙应用开发初识实践小案例 一、华为的“18N”产品战略 在华为HarmonyOS及全场景新品发布会上&#xff0c;华为介绍了华为“18N”三圈层全场景智慧生态解决方案&#xff0c;从而打造面向未来的全新生态&#xff0c;其中&#xff0c;1指的是手机&#xff0c;8指的是…

Python Django 连接 PostgreSQL 操作实例

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;我是彭涛&#xff0c;今天为大家分享 Python Django 连接 PostgreSQL 操作实例&#xff0c;全文3500字&#xff0c;阅读大约10分钟 在Web开发中&#xff0c;使用Django连接到PostgreSQL数据库是一种常见的选择。…

云计算:Vmware 安装 FreeNAS

目录 一、实验 1.Vmware 安装 FreeNAS 2.配置Web界面 二、问题 1.iSCSI如何限定名称 2.LUN和LVM的区别 一、实验 1.Vmware 安装 FreeNAS &#xff08;1&#xff09;环境准备 VMware Workstation 17 FreeNAS相关安装部署镜像: 官网地址&#xff1a; https://download…

磁力计LIS2MDL开发(3)----九轴姿态解算

磁力计LIS2MDL开发.3--九轴姿态解算 概述视频教学样品申请完整代码下载使用硬件欧拉角万向节死锁四元数法姿态解算双环PI控制器偏航角陀螺仪解析代码 概述 LIS2MDL 包含三轴磁力计。 lsm6ds3trc包含三轴陀螺仪与三轴加速度计。 姿态有多种数学表示方式&#xff0c;常见的是四元…

手机上的python怎么运行,python在手机上怎么运行

大家好&#xff0c;本文将围绕python程序如何在手机端运行展开说明&#xff0c;python程序如何在手机上运行是一个很多人都想弄明白的事情&#xff0c;想搞清楚手机上的python怎么运行需要先了解以下几个事情。 如何用手机编程Python&#xff1f; 1.QPython3&#xff1a;这是一…

Vue 自定义搜索输入框SearchInput

效果如下&#xff1a; 组件代码 <template><div class"search-input flex flex-space-between flex-center-cz"><input type"text" v-model"value" :ref"inpuName" :placeholder"placeholder" keyup.enter&…

MySQL日志管理,备份与恢复

备份的主要目的是灾难恢复&#xff0c;备份还可以测试应用、回滚数据修改、查询历史数据、审计等。 而备份、恢复中&#xff0c;日志起到了很重要的作用 MySQL日志管理是数据库管理中的一个重要方面&#xff0c;它可以用于诊断问题、监控性能、进行故障恢复等。MySQL主要有几种…

VSCode 常用的快捷键和技巧系列(2)

一、如何让VSCode工程树显示图标 第一步&#xff1a;安装 快捷键 CtrlP &#xff0c;输入 ext install vscode-icons &#xff0c;然后点击安装插件 第二步&#xff1a;配置 安装成功后&#xff0c;点击Reload重新加载。 然后配置&#xff0c;当前图标使用VsCode-Icons Go…

1264. 动态求连续区间和(树状数组---某个位置加上一个数/求在线(动态)前缀和/蓝桥杯)

题目&#xff1a; 输入样例&#xff1a; 10 5 1 2 3 4 5 6 7 8 9 10 1 1 5 0 1 3 0 4 8 1 7 5 0 4 8输出样例&#xff1a; 11 30 35 树状数组&#xff1a; 代码&#xff1a; #include<cstdio> #include<iostream> using namespace std;const int N100010; int n,…

【Python从入门到进阶】44、Scrapy的基本介绍和安装

接上篇《43.验证码识别工具结合requests的使用》 上一篇我们学习了如何使用验证码识别工具进行登录验证的自动识别。本篇我们开启一个新的章节&#xff0c;来学习一下快速、高层次的屏幕抓取和web抓取框架Scrapy。 一、Scrapy框架的背景和特点 Scrapy框架是一个为了爬取网站数…

Qt/C++音视频开发60-坐标拾取/按下鼠标获取矩形区域/转换到视频源真实坐标

一、前言 通过在通道画面上拾取鼠标按下的坐标&#xff0c;然后鼠标移动&#xff0c;直到松开&#xff0c;根据松开的坐标和按下的坐标&#xff0c;绘制一个矩形区域&#xff0c;作为热点或者需要电子放大的区域&#xff0c;拿到这个坐标区域&#xff0c;用途非常多&#xff0…

linux应急响应基础和常用命令

linux应急响应 linux应急响应基础和常用命令基于linux系统本身进行应急响应。 系统基础信息获取 获取linux服务器基本信息 命令&#xff1a; uname -a内存cpu信息 cat /proc/cpuinfo cat /proc/meminfo lscpu free -m lsmod #查看载入的模块信息进程查看 动态进程查看 …

将mjpg格式数转化成opencv Mat格式

该博客可以解决如下两个问题&#xff1a; 1、将mjpg格式数据转化成opencv Mat格式 2、v4l2_buffer 格式获取的mjpg格式数据转换成Mat格式。 要将 MJPEG 格式的数据转换为 OpenCV 的 Mat 格式&#xff0c;您可以使用 imdecode 函数。imdecode 函数可以将图像数据解码为 Mat 对象…