设计模式--单例模式

目录

介绍

单例模式的八种实现方式

饿汉式(静态常量)

优缺点说明:

饿汉式(静态代码块)

优缺点说明

懒汉式(线程不安全)

优缺点说明

懒汉式(线程安全 同步方法)

优缺点说明

懒汉式(线程安全 同步代码块)

优缺点说明

双重检查

优缺点说明

静态内部类

优缺点说明

枚举

优缺点说明:

单例模式的注意事项和细节说明


介绍

 所谓类的单例模式 就是采取一定的方法保证在整个软件系统中对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)

 比如 Hibemate的SessionFactory 它充当数据存储源的代理 并负责创建Session对象 SessionFactory并不是轻量级的 一般情况下 一个 项目通常只需要一个SessionFactory就够 这样就需要用到单例模式了

单例模式的八种实现方式

饿汉式(静态常量)

public class HungryMan1 {
    //1 构造器私有话 外部不能直接new对象调用
    private HungryMan1(){}

    //2 本类内部创建对象实例
    private final static HungryMan1 instance = new HungryMan1();
    //3 提供一个公有的静态方法 返回实例对象
    public static HungryMan1 getInstance(){
        return instance;
    }
}
public class HungryManTest {
    public static void main(String[] args) {
        HungryMan1 instanbce = HungryMan1.getInstance();
        HungryMan1 instabce2 = HungryMan1.getInstance();
        System.out.println(instanbce == instabce2);
        System.out.println(instanbce.hashCode());
        System.out.println(instabce2.hashCode());
    }
}

测试结果

优缺点说明:

(1) 优点: 这种写法比较简单, 就是在类装载的时候就完成了实例化 避免了线程同步问题

(2) 缺点: 在类装载的时候就完车实例化 没有达到lazy loading(懒加载)的效果 如果从始至终从未使用过这个实例 则会造成内存浪费

(3) 这种方式基于classloader(类装载器)机制避免了线程同步问题 不过,instance在类装载时就实例化 在单例模式中大多数都是调用getInstance方法 但是导致类装载的原因有很多种 因此不能确定有其他的方式(或者其他的静态方法) 导致类装载 这时候初始化instance就没有达到lazy loading的效果

(4) 结论: 推荐使用,但是可能会造成内存浪费

在jdk   java.lang.Runtime#getRuntime()的源码中就是使用的饿汉式的单例模式

 

饿汉式(静态代码块)

public class HungryMan2 {
    private HungryMan2(){}

    //在静态代码块中 创建单例对象
    static {
        instance = new HungryMan2();
    }

    //2 本类内部创建对象实例
    private final static HungryMan2 instance;
    //3 提供一个公有的静态方法 返回实例对象
    public static HungryMan2 getInstance(){
        return instance;
    }
}

测试同 "饿汉式静态常量"一致

优缺点说明

(1) 这种方式和上面的方法类似 只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候就执行了静态代码块中的代码 初始化类的实例 优缺点和上面是一样的

(2) 结论:推荐使用,但是可能造成内存浪费

懒汉式(线程不安全)

public class LazyMan1 {
    private static LazyMan1 instance;

    private LazyMan1(){};

    //提供一个静态的公有方法 当使用到该方法时 才会去创建 instance
    public static LazyMan1 getInstance(){
        if(instance==null){
            instance = new LazyMan1();
        }
        //等价于 jdk8 Optional写法
//        Optional.ofNullable(instance).orElseGet(()->new LazyMan1());
        return instance;
    }
}

测试同 "饿汉式静态常量"一致

优缺点说明

(1) 解决了lazy loading的问题 但是只能在单线程下使用 如果是多线程 可能会造成线程安全问题

(2) 如果再多线程下 一个线程进入了 if(instance==null)的判断语句块 还没来的及往下执行 另一个线程也通过了这个判断语句,此时就会产生多个实例 所以在多线程环境下不可以使用这种方式

(3) 结论: 在实际开发中 不要使用这种方式

懒汉式(线程安全 同步方法)

public class LazyMan2 {
    private static LazyMan2 instance;

    private LazyMan2(){};

    //提供一个静态的公有方法 加入同步处理的代码 解决线程安全问题
    public static synchronized LazyMan2 getInstance(){
        if(instance==null){
            instance = new LazyMan2();
        }
        return instance;
    }
}

测试同 "饿汉式静态常量"一致

优缺点说明

(1) 解决了线程不安全问题

(2) 效率低 每个 线程在想获得类的实例的时候 执行getInstance()方法都要进行同步 而其实这个方法只需要执行一次实例化代码就可以了 后面的想获得该类实例 直接return就可以了 方法进行同步效率太低

(3) 结论: 在实际开发中 不推荐使用

懒汉式(线程安全 同步代码块)

public class LazyMan3 {
    private static LazyMan3 instance;

    private LazyMan3(){};

    //提供一个静态的公有方法 加入同步处理的代码 解决线程安全问题
    public static LazyMan3 getInstance(){
        if(instance==null){
            synchronized(LazyMan3.class){
                instance = new LazyMan3();
            }
        }
        return instance;
    }
}

测试同 "饿汉式静态常量"一致

优缺点说明

(1) 这种方式 本意是想对 懒汉式(线程安全 同步方法)的改进,因为同步方法效率太低 改为同步产生实例化的代码块

(2) 但是这种同步不能起到线程同步的作用 跟 懒汉式(线程不安全) 遇到的情形一致 假如一个线程进入了 if(instance==null) 判断语句块 还没来得及往下执行 另一个线程也通过了这个判断语句 这时便会产生多个实例

(3) 结论: 在实际开发中 不要用这种方式

双重检查

public class LazyMan4 {
    private static volatile LazyMan4 instance;

    private LazyMan4(){};

    //提供一个静态的公有方法 加入双重检查代码 解决线程安全问题 同时解决懒加载问题
    public static synchronized LazyMan4 getInstance(){
        if(instance==null){
            synchronized(LazyMan4.class){
                if(instance==null){
                    instance = new LazyMan4();
                }
            }
        }
        return instance;
    }
}

测试同 "饿汉式静态常量"一致

优缺点说明

(1) double-check 概念是多线程开发中常使用到的 如代码中所示进行了两次 if(instance==null)检查 这样就可以保证线程安全了

(2) 这样 实例化代码只用执行一次 后面再次访问时 判断 if(instance==null),直接return实例化对象 也避免的反复进行方法同步

(3) 线程安全 延迟加载 效率较高

(4) 结论: 在实际开发中 推荐使用

静态内部类

public class LazyMan5 {
    private LazyMan5(){};

    //书写静态内部类 该类中有一个静态属性 LazyManInstance
    private static class LasyManInstance{
        private static final LazyMan5 INSTANCE = new LazyMan5();
    }

    //提供一个静态公有方法 直接返回LazyManInstance.INSTANCE
    public static synchronized LazyMan5 getInstance(){
        return LasyManInstance.INSTANCE;
    }
}

测试同 "饿汉式静态常量"一致

优缺点说明

(1) 这种方式采用了类装载的机制来保证初始化实例时只有一个线程

(2) 静态内部类方式在LazyMan5类被装载时并不会立即实例化 而是在需要实例时 调用 getInstance方法 才会装载LazyManInstance类 从而完成LazyMan5的实例化

(3) 类的静态属性只会在第一次加载类的时候初始化 所以在这里 是jvm帮我们保证了线程的安全性 在类进行初始化时 别的线程是无法进入的

(4) 解决了线程安全问题 利用静态内部类特点实现延迟加载 效率高

(5)结论: 推荐使用

枚举

public class HungryManTest {
    public static void main(String[] args) {
        LazyMan6 in = LazyMan6.INSTANCE;
        LazyMan6 in2 = LazyMan6.INSTANCE;
        in.sayHello();
        System.out.println(in==in2);
    }
}
enum LazyMan6 {
    INSTANCE;
    public void sayHello(){
        System.out.println("hello world!");
    }
}

测试:

优缺点说明:

(1) 借助jdk1.5中添加的枚举来实现单例模式 不仅能避免多线程同步问题 而且还能防止反序列化重新创建新的对象

(2) 这种方式是Effective java作者Josh Bloch推荐的方式

(3)结论:推荐使用

单例模式的注意事项和细节说明

(1) 单例模式保证了系统内存中该类只存在一个对象 节省了系统资源 对于一些需要频繁创建销毁的对象 使用单例模式可以提高系统性能

(2) 当想实例化一个单例类的时候 必须要记住使用响应的获取对象的方法 而不是使用new

(3) 单例模式使用的场景: 需要频繁的进行创建和销毁的对象 创建对象时耗时过多或耗费资源过多(重量级对象) 但又经常用到的对象 工具类对象 频繁访问数据库文件的对象(比如数据源 session工厂等)

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

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

相关文章

打怪升级之FPGA组成原理(LE部分)

FPGA芯片逻辑单元的原理 不论你使用哪一款FPGA芯片,其核心可编程逻辑单元都是从一段内存种按顺序读取执行并执行的过程。具体来说,FOGA芯片内部包括可编程逻辑块(LAB)、可配置输入输出单元(IOE)、时钟管理模块、嵌入式RAM(BRAN,在Cyclone IV…

PNAS: 这些病毒是原生动物基因组中的偷渡者

在对复杂单细胞微生物进行大规模研究时,奥地利因斯布鲁克大学生态学系的Christopher Bellas博士、Marie-Sophie Plakolb和Ruben Sommaruga教授发现了一个意外情况:微生物的基因组中找到超过30,000种先前未知的病毒DNA。这种“隐藏”的DNA可能允许宿主细胞…

字节跳动正式开源分布式训练调度框架 Primus

动手点关注 干货不迷路 项目地址:https://github.com/bytedance/primus 随着机器学习的发展,模型及训练模型所需的数据量越来越大,也都趋向于通过分布式训练实现。而算法工程师通常需要对这些分布式框架涉及到的底层文件存储和调度系统有较深…

基于 多态 的职工管理系统(Staff Management System)

目录 一、管理系统需求 作用:管理公司内所有员工的信息 分类:要显示每位员工的编号、姓名、岗位与职责 具体实现的功能: 二、创建管理 类 三、各个接口函数 1、菜单展示功能 2、 选择功能 3、创建员工功能 ①普通员工employee ②经理…

怎么批量把heic格式转化jpg,3招快速解决

怎么批量把heic格式转化jpg?heic是一种新型的图像文件格式,是苹果独家搞出来的一个图片格式,它小巧玲珑,而且图像质量超好,专门给iOS11系统用户用的。这种格式比老JPEG更厉害,不仅图片质量好,而…

【网络原理】应用层协议 与 传输层协议

✨个人主页:bit me👇 ✨当前专栏:Java EE初阶👇 目 录 🏉一. 应用层协议⚾️二. 传输层协议👒1. UDP 协议🌂2. 校验和👓3. TCP 协议 🏉一. 应用层协议 我们自己写的应用…

Bitmap 实现当前在线用户数量

Bitmap是什么? Bitmap是Redis中的一种数据结构,它是一个类似于位数组的数据结构,用于处理位数据。在Redis中,Bitmap是使用字符串来存储的,一个Byte可以存储8个二进制位,一个字符串可以存储232个二进制位&a…

【CocosCreator入门】CocosCreator组件 | ProgressBar(进度条)组件

Cocos Creator 是一款流行的游戏开发引擎,具有丰富的组件和工具,其中的ProgressBar组件是一种用于实现进度条效果的重要组件。它可以让我们在游戏中展示各种进度条效果,例如加载进度条、血条等。 目录 一、组件介绍 二、组件属性 三、脚本…

12. 图的进阶

12. 图的进阶 12.1 有向图 在实际生活中,很多应用相关的图都是有方向性的,最直观的就是网络,可以从A页面通过链接跳转到B页面,那么a和b连接的方向是a->b,但不能说是b->a,此时我们就需要使用有向图来解决这一类问题&#x…

【jvm系列-09】垃圾回收底层原理和算法以及JProfiler的基本使用

JVM系列整体栏目 内容链接地址【一】初识虚拟机与java虚拟机https://blog.csdn.net/zhenghuishengq/article/details/129544460【二】jvm的类加载子系统以及jclasslib的基本使用https://blog.csdn.net/zhenghuishengq/article/details/129610963【三】运行时私有区域之虚拟机栈…

为什么许多人吐槽C++11,那些语法值得我们学习呢?

致前行的人: 人生像攀登一座山,而找寻出路,却是一种学习的过程,我们应当在这过程中,学习稳定冷静,学习如何从慌乱中找到生机。 目录 1.C11简介 2.统一的列表初始化 2.1 {}初始化 …

git 常用命令及遇到问题

自己没事,把git常用命令做个记录总结。方便自己和初学者查看,本文针对初学者,如果你已经是工作多年高手,请跳过。 git的几个区认识,分别为工作区,缓存区,版本库。 工作区:包含.git…

【Unity VR开发】结合VRTK4.0:添加碰撞忽略器

语录: 最远的旅行,是从自己的身体到自己的心,是从一个人的心到另一个人的心。坚强不是面对悲伤不流一滴泪,而是擦干眼泪后微笑面对以后的生活。 前言: 模块化提供了一种允许两个或者多个对象忽略彼此碰撞的方法&#x…

揭秘移动云大会展区前沿科技

2023年4月25日-26日 我们苏州金鸡湖国际会议中心见! 1场重磅主论坛、10场分论坛、2600㎡展区 数字中国新未来 尽在2023移动云大会 2023移动云大会设有中国移动和合作伙伴两大展区,联合40余家优质合作伙伴,全方位展示移动云在自主能力、行…

vue yarn npm

2016年左右 ,facebook针对npm包管理工具存在的性能问题进行了针对性开发并发布了yarn新的node包开发管理工具,具体对比,同学们自行网上搜索资料对比。 配置 1、先下载好NodeJS,然后输入如下命令安装yarn npm install -g yarn 2、…

如何微调Segment Anything Model

文章目录 什么是SAM?什么是模型微调?为什么要微调模型?如何微调 Segment Anything 模型背景与架构创建自定义数据集输入数据预处理训练设置循环训练保存检查点并从中启动模型 下游应用程序的微调 随着 Meta 上周发布的 Segment Anything Mode…

线程等待其他线程执行同步类CountDownLatch

文章目录 前言核心原理源码解析同步源码分析await源码分析countDown源码分析 实战演示1、创建演示代码2、创建测试用例3、测试结果演示 写在最后 前言 大家都知道多线程在我们实际编码过程中运用很多,很多情况我们需要靠多线程来提升系统性能。但是有些时候我们需要…

C语言开发环境搭建及调试

C简介 可移植 标准C C/C (系统硬件操作的接口,windows,Linux不一样) 跨平台 Java Python 下载 去官网选择Visual Studio 2019下载 安装过程中勾选使用C的桌面开发 安装好之后点击创建新项目——空项目 位置最好放在根目录下&…

【vue2】近期bug收集与整理02

⭐【前言】 在使用vue2构建页面时候,博主遇到的问题难点以及最终的解决方案。 🥳博主:初映CY的前说(前端领域) 🤘本文核心:博主遇到的问题与解决思路 目录 ⭐数据枚举文件的使用⭐elementUI中分页组件使用的注意事项⭐…

OpenAI-ChatGPT最新官方接口《从0到1生产最佳实例》全网最详细中英文实用指南和教程,助你零基础快速轻松掌握全新技术(十一)(附源码)

Production Best Practices 生产最佳实例 前言Introduction 导言Setting up your organization 设置您的组织Managing billing limits 管理计费限额API keys API密钥Staging accounts 演示账户 Building your prototype 构建您的原型Additional tips 其它技巧 Techniques for i…