返回顶部
首页 > 资讯 > 后端开发 > JAVA >设计模式之单例模式(懒汉, 饿汉)
  • 168
分享到

设计模式之单例模式(懒汉, 饿汉)

单例模式设计模式java多线程线程安全 2023-09-05 06:09:08 168人浏览 安东尼
摘要

文章目录 一. 单例模式概述二. 单例模式的实现1. 饿汉模式2. 懒汉模式 一. 单例模式概述 单例模式是一种常用的软件设计模式, 该模式的主要目的是确保某一个类在内存中只能有一个实

文章目录

一. 单例模式概述

单例模式是一种常用的软件设计模式, 该模式的主要目的是确保某一个类在内存中只能有一个实例对象, 通过单例模式的方法创建的类在当前进程中只有一个实例对象.

常见的单例模式有两种:

  1. 饿汉式, 这里的 “饿” 意义表述不够清晰, 用 “急” 来表述意义更加容易联想一些, 饿汉式就是类一加载就创建对象, 给人一种 “特别急切” 的感觉.
  2. 懒汉式, 在使用时才去创建对象.

单例模式有 3 个特点:

  1. 单例类只有一个实例对象.
  2. 该单例对象必须由单例类自行创建.
  3. 单例类对外提供一个访问该单例的全局访问点.

我们知道static修饰的成员是属于类的, 也就是只有一份, 所以我们可以使用static修饰的成员变量保存实例对象的引用.

二. 单例模式的实现

1. 饿汉模式

单例模式中, 一个类只能有一个实例对象, 我们可以使用static成员变量来保存这个唯一实例对象的引用, 同时, 为了防止类被创建多个实例对象, 需要将类构造方法封装为private修饰的私有方法, 在使用该类时必须要得到该类的这个实例对象, 所以我们还需要创建一个对外公开的获取该唯一实例的方法getInstance.

class Singleton {    //使用static变量存储实例对象,类加载时创建对象.    private static Singleton intance = new Singleton();    //对外提供公开的方法获取该类的唯一实例.    public static Singleton getInstance() {        return intance;    }    //封装构造方法,防止该类被实例出新的对象    private Singleton() {}}

线程情况下不存在线程安全的问题, 我们需要考虑多线程情况下上面的代码是否存在线程安全问题, 重点要看读写情况下的线程安全问题, 上面的代码只有getInstance涉及到读操作, 所以上面的代码是线程安全的.

2. 懒汉模式

懒汉式与饿汉明显的区别就是对象创建的时机不同, 懒汉式要等到第一次使用实例对象的时候才会创建对象, 所以我们将上面的代码稍做修改写出下面的代码.

class SingletonLazy {    //在使用的时候创建对象,这里不初始化实例对象    private volatile static SingletonLazy instance = null;    //对外提供公开的方法获取该类的唯一实例,如果没有就创建    public static SingletonLazy getInstance() {        if (instance == null) {            instance = new SingletonLazy();        }        return instance;    }//封装构造方法,防止该类被实例出新的对象    private SingletonLazy() {}}

那么再来考虑上面的代码在多线程情况下是否有线程安全问题存在, 代码中的getInstance方法中存在两次读操作(一次比较一次返回)和一次写操作(修改intsance变量的值),第一次创建对象时(即instance=null)可能会存在多个线程进入判断语句, 此时就可能会创建出多个实例对象, 看下图理解:

img

所以上面写出来的代码可能会触发多次new操作, 显然就不是单例了, 也就是说上面写出来的懒汉式的代码是线程不安全的.

导致上面的线程安全问题的主要原因是读, 比较和写这三个操作不是原子的, 这就导致了t2读到的值可能是t1还没来得及写的(脏读), 所以我们只需要将代码中的读写操作加即可这里的问题, 得到下面的代码:

class SingletonLazy {    private volatile static SingletonLazy instance = null;    public static SingletonLazy getInstance() {        synchronized (SingletonLazy.class) {            if(instance == null) {                instance = new SingletonLazy();            }        }        return instance;    }    private SingletonLazy() {}}

但代码写到这里还是有问题的, 上述的线程安全问题只出现在instance没有初始化的时候, 如果instance已经有了实例对象, 后续调用getlnstanceinstance的值一定是非空的, 因此就会直接触发return, 相当于一个是比较操作, 一个是返回操作, 这就都是读操作了, 此时就不涉及线程安全问题了.

而上面的代码, 不论instance是否已经初始化, 每次调用getlnstance, 都需要进行加锁操作, 要知道加锁操作是有开销的, 在instance已经有了实例对象以后就完全没有必要去加锁了, 所以代码还需要修改, 在加锁前需要先判断一下instance是否已经初始化, 如果还没初始化就进行加锁, 如果实例对象创建了, 就不加锁了.

调整后的获取实例对象的代码如下:

public static SingletonLazy getInstance() {    if (instance == null) {        synchronized (SingletonLazy.class) {            if(instance == null) {                instance = new SingletonLazy();            }        }    }    return instance;}

到这里其实还没完, 还要考虑编译器优化造成的内存可见性和指令重排序的问题, 上面的代码中是有判断操作的(instance==null), 假设有很多线程, 都去进行getlnstance, 就会进行频繁的判断, 这时候就会有被优化的风险, 可能只有第一次读才是真正读了内存, 后续都是读寄存器/cache中的值, 这时候instance值变化时, 线程就感知不到了, 所以这里的内存可见性问题需要解决.

还有要注意这里的instance = new Singleton();操作, 拆分成三个步骤就是:

  1. 申请内存空间.
  2. 调用构造方法, 把这个内存空间初始化成一个合理的对象.
  3. 把内存空间的地址赋值给instance引用.

正常情况下,是按照123这个顺序来执行的, 但你防不住编译器的优化操作, 也就是指令重排序问题, 编译器为了提高程序效率, 可能就调整了代码执行顺序, 123这个顺序就可能变成132, 如果是单线程,123和132没有本质区别, 但是多线程环境下, 就会有问题了;

假设t1是按照132的步骤执行的, t1执行到13之后, 执行2之前,被切出cpu了, 然后t2来执行;

t1执行完3之后, 再从t2角度来看, 此处的引用就非空了, 此时此刻, t2就相当于直接返回了instance引用并且可能会尝试使用引用中的属性, 但是由于t1中的2操作还没执行完呢, t2拿到的是非法的对象(还没构造完成的不完整的对象), 这就可能会造成一些问题.

为了解决上述问题需要使用关键字volatile修饰instance变量来防止编译器优化, 从而保证内存可见性, 禁止指令重排序, 完整代码如下:

class SingletonLazy {    private volatile static SingletonLazy instance = null;    public static SingletonLazy getInstance() {        if (instance == null) {            synchronized (SingletonLazy.class) {                if(instance == null) {                    instance = new SingletonLazy();                }            }        }        return instance;    }    private SingletonLazy() {}}

来源地址:https://blog.csdn.net/Trong_/article/details/128551552

--结束END--

本文标题: 设计模式之单例模式(懒汉, 饿汉)

本文链接: https://lsjlt.com/news/394859.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 设计模式之单例模式(懒汉, 饿汉)
    文章目录 一. 单例模式概述二. 单例模式的实现1. 饿汉模式2. 懒汉模式 一. 单例模式概述 单例模式是一种常用的软件设计模式, 该模式的主要目的是确保某一个类在内存中只能有一个实...
    99+
    2023-09-05
    单例模式 设计模式 java 多线程 线程安全
  • 24种设计模式之单例模式(饿汉式、懒汉式)
    一、单例模式 单例模式( Singleton Pattern )是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例模式是创建型模式。单例模式在现实生活中应用也非常广泛,例如,总统,班主任等。J2EE标准中的Servl...
    99+
    2023-09-23
    java Powered by 金山文档
  • Java设计模式之单例模式实例详解【懒汉式与饿汉式】
    本文实例讲述了Java设计模式之单例模式。分享给大家供大家参考,具体如下:单例模式就是产生一个对象实例,供外外部访问。它的应用场景就是在这个类在全局真资源需要统一访问,否则会造成混乱时,才有必要设计成单例。懒汉式,就是在使用这个对象时,才去...
    99+
    2023-05-31
    java 设计模式 单例模式
  • java 单例模式(懒汉式与饿汉式)
    java 单例模式单例模式是一种常用的软件设计模式。在它的可信结构中只包含一个被实例化单例的特殊类。通过单例设计模式可以把整系统中的一个类只有一个实例。单例设计模式又分为两种方式,懒汉式和饿汉式。 (1)懒汉式,就是只有当调用getInst...
    99+
    2023-05-31
    java 单例模式 ava
  • C++单例模式的懒汉模式和饿汉模式详解
    目录懒汉模式饿汉模式线程安全的懒汉模式总结懒汉模式 懒汉模式在第一次用到类实例的时候才会去实例化,就是不到调用getInstance函数时,这个类的对象是一直不存在的。懒汉本身是线程...
    99+
    2024-04-02
  • 详解Java单例模式中的饿汉和懒汉模式
    目录一.什么是单例模式一.饿汉模式1.饿汉模式的概念2.饿汉模式代码3.多线程是否线程安全二.懒汉模式1.懒汉模式的概念2.单线程情况下的懒汉模式3.多线程情况下的懒汉模式一.什么是...
    99+
    2023-05-14
    Java单例模式 Java单例饿汉模式 Java单例懒汉模式
  • C++单例模式的懒汉模式和饿汉模式怎么实现
    本文小编为大家详细介绍“C++单例模式的懒汉模式和饿汉模式怎么实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“C++单例模式的懒汉模式和饿汉模式怎么实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。懒汉模式懒...
    99+
    2023-06-29
  • java 中单例模式饿汉式与懒汉式的对比
    java 中单例模式饿汉式与懒汉式的对比概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点。以前我们的做法是设置一个全局变量,也就是让它使得一个对象被访问。但是它不能防止你实例多个对象。这时我们可以让类自身负责保存它的唯一实例,这个...
    99+
    2023-05-31
    java 单例模式 中单
  • Java多线程案例之单例模式懒汉+饿汉+枚举
    目录前言:1.单例模式概述2.单例模式的简单实现2.1饿汉模式2.2懒汉模式2.3枚举实现单例模式前言: 本篇文章将介绍Java多线程中的几个典型案例之单例模式,所谓单例模式,就是一...
    99+
    2024-04-02
  • Java单例模式中的饿汉和懒汉模式怎么实现
    今天小编给大家分享一下Java单例模式中的饿汉和懒汉模式怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。什么是单例模式...
    99+
    2023-07-05
  • 一文详解Java的饿汉和懒汉设计模式
    目录饿汉设计模式懒汉设计模式饿汉和懒汉模式的区别本文主要讲述java的饿汉和懒汉设计模式 饿汉和懒汉设计模式的目的:使得该类的对象,只能有一个,不允许其他类,创建该类的对象。 饿汉设...
    99+
    2022-12-20
    Java饿汉 懒汉 设计模式 Java饿汉设计模式 Java 懒汉设计模式 Java 设计模式
  • Java单例模式的线程安全,饿汉和懒汉模式详解
    单例模式 创建唯一的一个变量(对象),在类中将构造函数设为protected或者private(析构函数设为相对应的访问权限),故外部不能实例化对象,再提供访问它的一个全局访问点,即...
    99+
    2024-04-02
  • Java的懒汉与饿汉模式怎么实现
    今天小编给大家分享一下Java的懒汉与饿汉模式怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。首先,我们先了解下单例模...
    99+
    2023-06-27
  • java中懒汉和饿汉模式有什么不同
    这期内容当中小编将会给大家带来有关java中懒汉和饿汉模式有什么不同,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Java的优点是什么1. 简单,只需理解基本的概念,就可以编写适合于各种情况的应用程序;2...
    99+
    2023-06-14
  • java懒汉式和饿汉式有什么不同
    懒汉式和饿汉式都是单例设计模式的实现方式,但它们有以下不同:1. 初始化时间:饿汉式是在类加载的时候就创建单例对象,而懒汉式是在第一...
    99+
    2023-08-30
    java
  • 使用java怎么实现一个饿汉模式单例
    今天就跟大家聊聊有关使用java怎么实现一个饿汉模式单例,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。常用的java框架有哪些1.SpringMVC,Spring Web MVC是一...
    99+
    2023-06-14
  • Android设计模式之单例模式
      1、单例模式常见情景   设计模式中,简单不过的是单例模式。先看看单例模式   Singleton模式可以是很简单的,它的全部只需要一个类可以完成(看看这章可怜的U...
    99+
    2022-06-06
    单例模式 Android
  • Java设计模式之单例模式
    目录什么是设计模式?单例模式是什么?单例模式设计的原则是什么?Java实现单例模式的5种方式?懒汉饿汉静态内部类双重校验锁DCL(Double Check Lock)枚举(num)总...
    99+
    2024-04-02
  • C#设计模式之单例模式
    单例模式也是创建型模式的一种,也是23种设计模式中比较简单的一种。见名思意,在整个软件系统中,只有某个类型的一个对象,并且访问他的地方也只有一个,也就是只有一个全局对象访问点,这个实...
    99+
    2024-04-02
  • python设计模式之单例模式
    单例模式是一种创建型设计模式,它确保一个类有且只有一个特定类型的对象,并提供全局访问点。其意图为: 确保类有且只有一个对象被创建 为对象提供一个访问点,使程序可以全局访问该对象 控制共享资源的并行访问 简单理解:单例...
    99+
    2023-01-30
    模式 python
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作