单例模式和工厂模式

这篇具有很好参考价值的文章主要介绍了单例模式和工厂模式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

今日良言:关关难过关关过,步步难行步步行

一、单例模式

1.饿汉模式

2.懒汉模式

二、工厂模式


今日良言:关关难过关关过,步步难行步步行

单例模式和工厂模式,单例模式

一、单例模式

首先来解释一下,什么是单例模式。

单例模式也就是单个实例(对象)。在有些场景中,只能创建出一个实例,不应该创建多个实例。

单例模式,就是针对上述的需求场景进行了个更强制的保证,通过巧用 java 现有的语法,达成了某个类 只能被创建出一个实例,这样的效果.(当程序猿不小心创建了多个实例,就会编译报错)。

单例模式最常见的两种就是:饿汉模式和懒汉模式。

1.饿汉模式

代码如下:

class Singleton{
    // 在此处就把实例给创建出来了
    private volatile static Singleton instance = new Singleton();
    // 如果需要使用这个唯一的实例,统一通过这个方法获取
    public static Singleton getInstance() {
        return instance;
    }
    // 为了避免Singleton 类不小心被赋值出多份,将构造方法设置成private.
    // 此时就无法通过new 的方式来创建这个Singleton 实例了。
    private Singleton(){}
}
public class Exercise {
    public static void main(String[] args)  {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println(s1 == s2);
    }
}

 单例模式和工厂模式,单例模式

在类加载阶段就将实例创建好了,这种效果就给人一种“特别急切”的感觉。

被static修饰表示这个属性和实例无关,而是和类相关。

Java 代码中的每个类,都会在编译完成后得到.class 文件,JVM 运行时就会加载这个.class文件,读取其中的二进制指令,并且在内存中构造出对应过的类对象(形如:Singleton.class),具体的类加载可以阅读博主之前写的博客:

深度剖析JVM三个面试常考知识点_程序猿小马的博客-CSDN博客

由于 类对象在一个 Java 进程中是唯一的,因此这个类对象的内部的类属性也是唯一的。

static 在这里的作用有两个:

1)static 保证这个实例唯一。

2)static 保证这个实例确实在一定的时机被创建出来。

static 属于这个实现方式中的灵魂角色。

2.懒汉模式

代码实现:

class SingletonLazy {
    private static SingletonLazy instance = null;
    public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
    // 构造方法设置成私有的
    private SingletonLazy(){}
}
public class Exercise {
    public static void main(String[] args)  {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();
        System.out.println(s1 == s2);
    }
}

单例模式和工厂模式,单例模式

这个实例并非是类加载阶段创建,而是真正第一次使用的时候才去创建, 如果不用就不创建了。

上述写的饿汉模式和懒汉模式是在单线程情况下的代码,如果在多线程下调用getInstance 是否是线程安全的呢?

答案是:一个是线程安全的,一个是线程不安全的。

饿汉模式是线程安全的,因为饿汉模式的 getInstance 方法只涉及到“读操作”。

懒汉模式是线程不安全的,因为懒汉模式的 getInstance 方法既有读操作又有写操作。

线程安全问题的详细解释,博主在之前的博客中有提到:

线程安全问题_程序猿小马的博客-CSDN博客

这里如果在多线程情况下调用懒汉模式的 getInstance 方法,会发生多次 new 操作,显然就不是单例了。

那么,如何让上述懒汉模式能够成为线程安全的呢?

加锁!!!

上述线程安全问题本质上是 修改操作不是原子的,因此,需要保证这个修改操作是原子的。

修改代码如下:

单例模式和工厂模式,单例模式

 此时,把锁加到外面,保证了读操作和修改操作是一个整体。

但是,代码写到这里,还有问题,上述这种写法,就导致了每次 getInstance 都需要加锁,加锁操作都是有开销的,仔细考虑一下,这里真的需要每次都加锁吗?

显然不是,这里的加锁只是在new出对象之前加上是有必要的,一旦对象 new 完以后,后续调用 getInstance ,此时 instance 一定是非空的,因此会直接 return。

基于上述讨论,就可以给刚才的代码加上一个判定:

如果对象还没创建才加锁,如果对象已经创建过了,就不加锁了。

修改代码如下:

单例模式和工厂模式,单例模式

此时,这里就不再是无脑加锁了,而是满足了特定条件之后,才真正加锁。 

注意:

这两个if 的作用不一样,第一个if 判断是否要加锁,第二个if 判断是否要创建对象。

加锁操作可能会引起线程阻塞,当执行到锁结束之后,执行到第二个 if 的时候,第二个 if 和第一个 if 之间可能已经隔了很久的时间了,instance 变量可能已经被别的线程给修改过了,所以需要第二次 if 判断当前线程是否需要创建对象。

上述代码其实还存在问题: 内存可见性问题以及指令重排序

关于这个问题,博主之前的博客也有详细介绍:

线程安全问题_程序猿小马的博客-CSDN博客

单例模式和工厂模式,单例模式

内存可见性问题:

假设有很多线程都去进行 getInstance ,这个时候,可能会存在被优化的风险(只有第一次读的时候,才真正的读了内存,后续都是读寄存器)

指令重排序:

instance = new SingletonLazy();  

这个操作可以拆分为三个步骤:

1)申请内存空间。

2)调用构造方法,把这个内存空间初始化成一个合理的对象。

3)把内存空间的地址赋值给 instance 引用。

正常情况下,是按照 1 2 3 这个顺序执行代码,但是编译器存在指令重排序问题,编译器为了提高程序效率,会调整代码执行顺序, 1 2 3 可能就变成了 1 3  2 ,如果是单线程下,1 2 3 和 1 3 2 没有本质区别,但是多线程下就会出现问题了。 

假设线程 t1 是 按照 1 3 2 的步骤执行的,t1 执行到 1 3 之后,执行 2 之前,被切除 cpu ,此时 t2执行,(当 t1 执行完 3 之后,t2 看起来此处的引用就非空了)此时此刻,t2 就相当于直接返回了 instance 引用,并且可能会尝试使用引用中的属性,但是由于 t1 中的 2 操作还没执行完呢,t2 拿到的是非法的对象,还没构造完成的不完整的对象。

因此,需要解决上述问题,使用 volatile  !!!

修改代码如下:

单例模式和工厂模式,单例模式

 懒汉模式完整代码如下:

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(){}
}
public class Exercise {
    public static void main(String[] args)  {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();
        System.out.println(s1 == s2);
    }
}

二、工厂模式

先来解释一下什么是工厂模式。

工程模式用一句话概括:使用普通的方法,来代替构造方法创建对象。

为什么要代替呢? 这是因为构造方法有坑,坑就体现在,如果只构造一种对象,好办,如果要构造多种不同的情况,就不好办了。

举个例子:

假设现在有一个类,表示平面上的一个点

单例模式和工厂模式,单例模式

 上述构造方法表示使用笛卡尔坐标系提供的坐标来构造点。

如果这里假设再使用极坐标来构造点,代码如下:

单例模式和工厂模式,单例模式

很明显,这个代码存在问题,正常来说,多个构造方法,是通过“重载”的方式来提供的,重载要求的是 方法名相同,参数的个数或者类型不同。

为了解决这个问题,就可以使用工厂模式:

单例模式和工厂模式,单例模式

 普通方法的方法名没有限制,因此有多种方式构造,使用不同的方法名即可。

以上就是单例模式和工厂模式的介绍。文章来源地址https://www.toymoban.com/news/detail-627569.html

到了这里,关于单例模式和工厂模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 了解单例模式,工厂模式(简单易懂)

    什么是单例? 系统运行期间有且仅有一个实例。 为什么要有单例?系统中的一些对象只需要初始化一次即可,例如:KTV中的播放器或者初始化系统参数 单例模式的要求: 一个类只有一个实例 只提供私有的构造器。 必须自己创建这个实例 定义该类的静态私有对象。 必须自

    2024年02月10日
    浏览(32)
  • 单例模式和多例模式和工厂模式

    学习目标 能够使用单例设计模式设计代码 内容讲解 正常情况下一个类可以创建多个对象 如果说有时一个对象就能搞定的事情 , 非要创建多个对象 , 浪费内存!!! 1.1 单例设计模式的作用 单例模式,是一种常用的软件设计模式。通过单例模式可以保证项目中,应用该模式的这个

    2024年02月12日
    浏览(28)
  • 设计模式-创建型模式(单例、工厂、建造、原型)

    设计模式:软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。 面向对象三大特性:封装、继承、多态。 面向对象设计的SOLID原则: (1)开放封闭原则:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情

    2024年02月08日
    浏览(52)
  • 【java设计模式】创建型模式介绍(工厂模式、抽象工厂模式、单例模式、建造者模式、原型模式)

    简介 本文介绍Java设计模式中创建型模式的五种 一、工厂模式 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 工厂模式提供了一种将对象的实例化过程封装在工厂类中的方式。通过使用工

    2024年02月16日
    浏览(45)
  • Python入门【​编辑、组合、设计模式_工厂模式实现 、设计模式_单例模式实现、工厂和单例模式结合、异常是什么?异常的解决思路 】(十七)

    👏作者简介:大家好,我是爱敲代码的小王,CSDN博客博主,Python小白 📕系列专栏:python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀 🔥如果感觉博主的文章还不错的

    2024年02月14日
    浏览(42)
  • java基础之设计模式(单例模式,工厂模式)

    是一种编码套路 单例模式 一个类只能创建一个实例 饿汉式 直接创建唯一实例 缺点: 有可能浪费空间 懒汉式 在获取实例是创建唯一对象 缺点: 线程效率慢 懒汉式-进阶版 在懒汉式的基础上,利用同步代码块结合二次校验提高执行效率 工厂模式 是一种底层技术,通常用于底层框

    2024年01月18日
    浏览(43)
  • 【2023-05-09】 设计模式(单例,工厂)

    单例模式 顾名思义,就是整个系统对外提供的实例有且只有一个 特点: ​ 1、单例类只有一个实例 ​ 2、必须是自己创建唯一实例 ​ 3、必须给所以对象提供这个实例 分类:一般分为饿汉式单例(直接实例化)和懒汉式单例(使用时才实例化) 饿汉式单例 懒汉式单例 同步

    2024年02月03日
    浏览(37)
  • day39 注解 设计模式(单例模式和工厂模式)

    @Target    @Target(ElementType.TYPE) @Target({                 ElementType.TYPE,                          ElementType.METHOD}) //确定自定义的注解的使用范围 type为类  method为方法  field为属性 @Retention @Retention()  //(不添加参数默认是class) @Retention(RetentionPolicy.CLASS)// 保留到

    2024年02月09日
    浏览(36)
  • Java中23种设计模式-单例模式--工厂模式

    加油,新时代打工人! 23种设计模式定义介绍 Java中23种设计模式-单例模式 Java中23种设计模式-单例模式2–懒汉式线程不安全 Java中23种设计模式-单例模式2–懒汉式2线程安全 Java中23种设计模式-单例模式–饿汉式 定义: 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一

    2024年02月21日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包