设计模式 - 单例模式(一)

news2024/11/17 11:46:03

单例模式

  • 一 官方定义
  • 二 单例模式八种方式
    • 2.1 饿汉式(静态常量)
      • 代码案例
      • 案例分析
    • 2.2 饿汉式(静态代码块)
      • 代码案例
      • 案例分析
    • 2.3 懒汉式(线程不安全)
      • 代码案例
      • 案例分析
    • 2.4 懒汉式(线程安全,同步方法)
      • 代码案例
      • 案例分析
    • 2.5 懒汉式(线程不安全,同步代码块)
      • 代码案例
      • 案例分析
    • 2.6 双重检查 (推荐使用)
      • 代码案例
      • 案例分析
        • 优点
        • 可能出现的问题
        • 扩展 - Volatile
    • 2.7 静态内部类 (推荐使用)
      • 代码案例
      • 案例分析
    • 2.8 枚举方式
      • 代码案例
      • 案例分析
    • 三 注意事项
    • 四 单例模式的使用场景

一 官方定义

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

Spring 中的 bean 默认都是单例模式,每个bean定义只生成一个对象实例,每次 getBean请求获得的都是此实例

二 单例模式八种方式

单例模式的八种实现方式,如下所示

  • 饿汉式(静态常量)
  • 饿汉式(静态代码块)
  • 懒汉式(线程不安全)
  • 懒汉式(线程安全,同步方法)
  • 懒汉式(线程安全,同步代码块)
  • 双重检查
  • 静态内部类
  • 枚举方式

2.1 饿汉式(静态常量)

代码案例

class Singleton {
    //一:构造器的私有化    防止外部用构造器...
    private Singleton() {
    }

    //二:类的内部创建对象    final static
    private static final Singleton singleton = new Singleton();

    //三:对外提供公共的静态方法    返回该类唯一的对象实例
    public static Singleton getInstance() {
        return singleton;
    }
}

案例分析

//案例演示 - 饿汉式
public class SingletonDemo {
    public static void main(String[] args) {
//      方式一:静态常量
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance == instance1); //true
        System.out.println(instance.hashCode());
        System.out.println(instance1.hashCode());
}

在这里插入图片描述
通过结果可以发现返回的是同一个对象所以单例模式是实现的。

写法分析
优势: 简单 避免多线程的同步问题
劣势 : 没有达到懒加载的效果 内存的浪费

2.2 饿汉式(静态代码块)

代码案例

//方式二:静态代码块的方式
class Singleton {

    //构造器私有化
    private Singleton() {
    }

    //类的内部创建对象
    private static final Singleton singleton;

    static {
        singleton = new Singleton();
    }

    //对外提供公共的静态的方法
    public static Singleton getInstance() {

        return singleton;
    }
}

案例分析

优势: 简单 避免多线程的同步问题
劣势 : 没有达到懒加载的效果 内存的浪费

2.3 懒汉式(线程不安全)

代码案例

class Singleton{

    //构造器私有化
    private Singleton(){}

    //类的内部提供对象
    private static Singleton singleton;

    //对外提供公共的静态方法的时候,来判断
    public static Singleton getInstance(){

        if (singleton == null){

            singleton = new Singleton();
        }

        return singleton;
    }

}

案例分析

优势:起到了懒加载的效果 不会造成内存浪费

在使用到的时候才会创建对象。判断有无对象,有则返回,无创建后再返回

劣势:只能在单线程下使用,多线程情况下线程不安全 不推荐这种方式的
在这里插入图片描述

① 在多线程的情况下,有一个对象进入if判断通过,还没执行到创建对象这一步骤时
② 有另外一个对象也进入了if判断,也通过了。
此时就会出现多个实例,造成线程不安全。

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

在获取对象的静态方法上添加 synchronized 关键字,实现同步方法。解决线程不安全问题。

代码案例

//加入同步处理  同步方法
class Singleton{

    //构造器私有化
    private Singleton(){}

    //类的内部提供对象
    private static Singleton singleton;

    //对外提供公共的静态方法的时候,来判断
    public static synchronized Singleton getInstance(){

        if (singleton == null){

            singleton = new Singleton();
        }

        return singleton;
    }

}

案例分析

解决了线程安全问题,但是效率太低

每一个线程需求拿实例的时候都要在外面等候另一线程处理完

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

将实例化对象过程放入同步代码块中

代码案例

//加入同步处理 - 同步代码块的方式  不推荐的
class Singleton{

    //构造器私有化
    private Singleton(){}

    //类的内部提供对象
    private static Singleton singleton;

    //对外提供公共的静态方法的时候,来判断
    public static Singleton getInstance(){

        if (singleton == null){

            synchronized (Singleton.class){

                singleton = new Singleton();
            }

        }

        return singleton;
    }

}

案例分析

不推荐的,解决不了线程的安全问题

这种方式本意是想解决同步方法的问题。但是我们分析一下。在多线程情况下还是有可能创建多个实例的问题。

2.6 双重检查 (推荐使用)

代码案例

class Singleton{

    private Singleton(){}
    //禁止指令重排
    private static volatile Singleton singleton;
    //加入双重检查机制
    public static Singleton getInstance(){
        if (singleton == null){
            synchronized (Singleton.class){
                if (singleton == null){
                    singleton = new Singleton();
                }
            }
        }

        return singleton;
    }
}

案例分析

优点

线程安全

解决了线程安全问题
① 多线程时,若两个线程同时满足第一层非空判断,等待调用同步代码块。
② 由于添加了synchronized ,当第一个线程进来时,发现Singleton对象为空,进行创建对象并返回。
③ 当另外一个线程调用同步代码块时,进行再次对象非空判断,发现对象已经创建成功。不在执行创建对象流程,返回已有对象

懒加载

使用到对象时才创建,不会造成内存的浪费

效率很高

先进性了singleton 是否为空的判断,singleton 如果不为空直接返回结果。倘若无第一层判空,多线程时每次都要进行synchronized 等待其他线程处理结束,才能进入内部非空判断,效率相对低。

可能出现的问题

我们认为的 new Singleton() 操作

1)分配内存地址 M
2)在内存 M 上初始化Singleton 对象
3)将M的地址赋值给 instance 对象

JVM编译优化后(指令重排)可能的 new Singleton() 操作

1)分配内存地址 M
2)将M的地址赋值给instance变量
3)在内存M上初始化 Singleton 对象

这就有可能出现空指针异常
异常发生过程
在这里插入图片描述
解决方式:关键字 Volatile 来禁止指令重排

扩展 - Volatile

轻量级的同步机制 (低配版) 没有保证原子性
三大特性
保证可见性
其中一个线程修改了主内存共享变量的值,要写回主内存,并要及时通知其他线程可见
没有保证原子性
没法(不能保证)不可分割,完整,要么同时成功,要么同时失败
禁止指令重排
和底层内存屏障相关 避免多线程下出现指令乱序的情况

扩展-线程切换

Java的一条语句对应的cpu指令可能是多条,其中任意一条cpu指令在执行完都可能发生线程切换
count += 1,对应cpu 指令如下:

1)将变量count从内存加载到cpu寄存器
2)寄存器中 +1
3)将结果写入内存(缓存机制写入的可能是cpu而不是内存)
在这里插入图片描述

2.7 静态内部类 (推荐使用)

代码案例

class Singleton{

    private Singleton(){}

    private static class SingletonInstance{

        public static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance(){

        return SingletonInstance.INSTANCE;
    }
}

案例分析

①当Singleton类加载时,静态内部类是不会加载的。
②只有调用getInstance方法用到了SingletonInstance.INSTANCE静态变量时导致SingletonInstance静态内部类进行加载。

不会出现线程安全问题
JVM来帮我们保证了线程的安全性
利用静态内部类的特点,效率也很高,实际开发中推荐使用的

2.8 枚举方式

代码案例

public class EnumDemo {
    public static void main(String[] args) {

        //验证其正确性
        Singleton instance = Singleton.INSTANCE;
        Singleton instance1 = Singleton.INSTANCE;
        System.out.println(instance == instance1); //true
        System.out.println(instance.hashCode());
        System.out.println(instance1.hashCode());

    }
}

enum Singleton{
    INSTANCE; //属性
}

案例分析

不仅可以避免线程安全问题 还可以防止反序列化重新创建对象。推荐使用

三 注意事项

  1. 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
  2. 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new

四 单例模式的使用场景

  • 对于一些需要频繁创建销毁的对象
  • 重量级的对象(创建对象时耗时过多,或者耗费资源过多)
  • 经常使用到的对象
  • 工具类对象
  • 数据源,session。。。。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据要求说明书(GB856T——88)基于协同的在线表格forture-sheet

数据要求说明书 1引言 1.1编写目的 本份数据要求说明书详细的提供了系统中各个数据的流向,是设计数据库的关键所在。为以后的编码以及测试提供一份可靠的依据。 预期的读者:系统开发人员、系统测试人员、系统维护人员 1.2背景 待开发的数据库名称&a…

揭秘百度智能测试在测试定位领域的实践

以前,我们介绍了测试活动测试输入、测试执行、测试分析、测试定位和测试评估五个步骤中测试输入、执行、分析、评估的智能化研究和实践,本文重点介绍测试定位环节的智能化实践。 测试定位的主要作用是在构建失败或问题发生后,快速给出产生该现…

机器学习之回归

回归算法 线性回归 求解线性回归方法 正规方程梯度下降 迭代 API sklearn.linear_model.LinearRegression 正规方程优化fit_intercept 是否计算偏置量,没有的化经过原点属性 coef_ 回归系数intercept_ 偏置量 sklearn.linear_model.SGDRegressor 使用随机梯度…

转行了!文科生转程序员的外包工作经历分享

01 种子 我是一名文科生,法律专业,武汉某 211 ,入这行纯属巧合。 大三下半年,大家纷纷准备秋招,我去校园招聘会上溜达了一圈,好奇而去,丧气而归。 或许是因为大学三年过得太过安逸(宅在宿舍打…

C#语言实例源码系列-实现本地磁盘目录

专栏分享点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册 👉关于作者 众所周知,人生是一个漫长的流程,不断克服困难,不断反思前进的过程。在这个过程中…

POMO: Policy Optimization with Multiple Optima for Reinforcement Learning学习笔记

文章目录摘要零、一些基础1.梯度近似2.策略梯度定理3.REINFORCE4.REINFORCE with Baseline5.REINFORCE Actor-Critic一、介绍二、相关工作(1)深度强化学习构建法(2)推理技术(3)深度强化学习改善法三、激励四…

制作 iOS 证书

准备工作 您需要 注册并加入 Apple Developer Program,才能在 App Store 上架应用。请准备一台 macOS 系统的电脑,用于证书制作。 创建 App ID 登录 Apple Developer,选择 Certificates, Identifiers & Profiles 选项。 选择 Identifi…

自学编程和计算机科班出身的差别在哪里

前不久逛知乎的时候看到一个问题:自学编程和计算机科班出身的差别在哪里? 自己回答了一下,获得了比较多的点赞和评论,在这里也分享给大家。 985 通信专业学长,转行程序员,聊一聊我的看法:说一千…

k8s之Job 与 CronJob

写在前面 语雀原文阅读效果更佳: 5、Job与CronJob 语雀 《5、Job与CronJob》 Job 与 CronJob 接下来给大家介绍另外一类资源对象:Job,我们在日常的工作中经常都会遇到一些需要进行批量数据处理和分析的需求,当然也会有按时间来…

RK3568平台开发系列讲解(工具命令篇)常用 GIT 命令汇总

🚀返回专栏总目录 文章目录 一、GIT BASICS二、GIT DIFF三、UNDOING CHANGES四、REWRITING GIT HISTORY五、GIT BRANCHES六、REMOTE REPOSITORIES七、GIT CONFIG八、GIT LOG九、GIT RESET十、GIT PULL十一、GIT PUSH沉淀、分享、成长,让自己和他人都能有所收获!😄 一、GI…

基于51单片机的酒精气体检测器设计

使用说明: 上电以后,需要预热一段时间,此时lcd显示Loading... (预热过程为电压先上升后下降的改成,通过检测电压来检测是否预热完成) , 预热完成后显示酒精浓度,按下按下按键key1即…

MySQL:互联网公司常用分库分表方案汇总

本文目录 一、数据库瓶颈 IO瓶颈 CPU瓶颈 二、分库分表 水平分库 水平分表 垂直分库 垂直分表 三、分库分表工具 四、分库分表步骤 五、分库分表问题 非partition key的查询问题 非partition key跨库跨表分页查询问题 扩容问题 六、分库分表总结 七、分库分表示例 …

因特网概述

目录1 网络、互连网(互联网)和因特网2 因特网发展的三个阶段3 因特网的标准化工作4 因特网的组成1 网络、互连网(互联网)和因特网 网络(Network)由若干结点(Node)和连接这些结点的链…

2022年山东省职业院校技能大赛高职组“网络系统管理”赛项规程

2022年山东省职业院校技能大赛高职组“网络系统管理”赛项规程一、赛项名称赛项名称:网络系统管理赛项组别:高职组赛项类别:电子与信息大类二、竞赛目的本赛项旨在借鉴世界技能大赛的办赛理念与技术规程,通过竞赛让参赛选手经历一…

论文导读 | 关于内存子图匹配算法的调研

前言 近年来,图数据结构在学术界和工业界的应用越来越广泛,包括社交网络分析、道路分析、化学分子合成、生物蛋白质网络分析、金融欺诈检测等等。子图匹配(Subgraph Matching)是图分析领域研究的一个重要课题,其旨在一…

批发进销存软件哪个好用?求测评

产品产量增加,在其他条件不变的情况下,就会形成规模效应,这样产品的生产成本就会降低。批发市场就可以对接大批量生产和出售单价更低的商品。而零售业往往也会采取批发货物的方式来降级成本。但是产品种类多,对于的供应商和客户也…

FactoryBean

FactoryBean ①简介 FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制&…

网友:我30多岁了,现在转行学编程来得及吗?

这些年,互联网行业的变化发展很快,很多公司也是借助互联网发展的大趋势发展的非常好。 水涨船高,行业好,意味着互联网行业的从业者的工资也就跟着高,很多互联网行业刚入门的月薪基本都已经过万了,一些传统行…

Android设计模式详解之命令模式

前言 命令模式是行为型设计模式之一; 定义:将一个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;对请求排队或者记录日志,以及支持可撤销的操作; 使用场景: 需要抽象出待执行…

自动化测试高手-价值篇

自动化测试的最终交付价值是什么:自动化测试项目的最终交付价值是它产生的收益,也就是投入回报率比,ROI,一个成功的自动化测试项目必然是获得了高ROI的收益。 自动化测试高手就是要做出成功的自动化测试项目,不仅要写代…