【JavaEE】Java设计模式-单例模式(饿汉式与懒汉式)

简介: 【JavaEE】Java设计模式-单例模式(饿汉式与懒汉式)

1.设计模式是啥?

设计模式是前人经过总结,通过对不同应用场景应该运用何种方法解决问题的模式。我们可以将它看成NBA中的战术(例如:电梯门战术),在应对不同的队伍时,需要运用不同的战术,针对不一样的队伍用不一样的战术才能更高效的得分,这就相当于对不同的问题有固定的模板免得去思考,可以直接去使用。故可认为:它是解决待定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以重复使用。其目的是为了提高代码的可重用性、代码的可读性和代码的可靠性。


2.单例模式

2.1什么是单例模式

这个就看字面意思就很好理解,就是只能有一个例子,也就是该类只能new一个对象。

官方的说就是:这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

目前单例模式有两种:1.饿汉式 2.懒汉式

如何实现单例模式呢?我们就从者两个模式的具体实现来说明吧!

2.2饿汉模式

饿汉式单例模式实现步骤:

1.将构造器私有化

 解释:防止用户构造新对象

2.在类中创建好一个对象

 解释:因为我们把构造器进行了私有化操作,但我们单例模式中需要有个对象,因此我们需要在类中实例化好一个对象。

3.创建一个方法,或者类中创建好的对象

 解释:因为我们外部不能创建对象,但是我们类中有创建好的对象,因此我们需要一个方法来将对象给取出来。

代码实现:

class Singleton {
    private static Singleton instance=new Singleton();
    public static Singleton getS() {
        return  instance;
    }
    private  Singleton(){};
}
public class testDemo1 {
    public static void main(String[] args) {
        Singleton singleton=Singleton.getS();
    }
}

注意:

观察代码,有几个细节需要注意;因为用户不可以创建对象,因此我们需要在类中创建好对象,不能创建对象,就意味着我们不能通过对象引用类中的成员方法与成员变量,即在类中new对象时和获得new好的对象时需要用静态成员变量和静态成员方法,通过类名来访问

2.3懒汉模式

懒汉式单例模式实现步骤:

1.将构造器私有化

 解释:防止用户构造新对象

2.在类中创建好一个对象

 解释:因为我们把构造器进行了私有化操作,但我们单例模式中需要有个对象,因此我们需要在类中实例化好一个对象。

3.创建一个方法,或者类中创建好的对象

 解释:因为我们外部不能创建对象,但是我们类中有创建好的对象,因此我们需要一个方法来将对象给取出来。

class  SingleLazy{
    private static SingleLazy instance=null;
    public static SingleLazy getInstance() {
        if(instance==null) {
            instance=new SingleLazy();
        }
        return instance;
    }
    private SingleLazy() {
    }
}
public class testDemo2 {
    SingleLazy singleLazy=SingleLazy.getInstance();
}

注意:

观察代码,有几个细节需要注意;因为用户不可以创建对象,因此我们需要在类中创建好对象,不能创建对象,就意味着我们不能通过对象引用类中的成员方法与成员变量,即在类中new对象时和获得new好的对象时需要用静态成员变量和静态成员方法,通过类名来访问。


与饿汉模式不同之处时,懒汉模式只有在调用到这个对象时,对象才得以创建,在没有调用之前,创建出来的对象时null,懒汉模式要比饿汉模式节省一些不必要的资源。  


3.懒汉模式与饿汉模式的区别

3.1.线程安全方面

饿汉式天生就是线程安全的,因为饿汉模式当中仅仅只有读操作,可以直接用于多线程而不会出现问题,懒汉式本身是非线程安全的,懒汉模式线程不安全是因为他在创造对象时会产生指令重排序。

3.2.资源加载和性能

饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。

懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟。


4.如何保证懒汉模式的线程安全

首先我们要了解什么是线程不安全:

  1. 1. 抢占式执行
  2. 2.修改操作,不是原子性的
  3. 3.内存可见性,引发的线程不安全
  4. 4.指令重排序引发的线程不安全
  5. 5.多线程修改同一个变量引发的线程不完全

我们对标一下在懒汉模式中有几条是符合上方条件的



在懒汉模式getInstance()方法中,对instance对象即包括了读操作,也包括了写操作;在操作时,会发生指令重排序这对于多线程是不完全的。

这样说不是很只管,我们画图来说明(这里的例子仅代表一种情况):



这种情况我们如何避免呢?很好整,不同的问题,我们就不同的方案来解决,这时的解决方法就是给代码加锁



这样我们就可以阻止指令重排序,让写操作在同一个线程内完成了!


真的就完成了吗?


并非如此,试想:在instance初始化之前,还是instance初始化之后,都会导致阻塞,存在大量的锁竞争,因此导致程序运行速度减慢(哪有一本万利的生意🐣)。


因此对于防止过多的锁竞争,我们需要再次改进代码:再加入一个判断(虽然有两个相同的if判断,但是两个判断的初心不同),判断instance是否初始化,如果已经初始化过了,就不需要再次加锁了。



还有一个问题没有解决就是内存可见性的问题。

如果有多个线程都调用getInstance()方法,就会有大量的读instance操作,我们知道对于访问内存与寄存器,访问寄存器的速度要比内存高几个数量级;因此,编译器就会自作主张优化掉访问内存的操作;即:将读内存的操作优化为读寄存器的操作。


为了保证内存可见性,我们需要将 在创建instance对象的语句上加上volatile关键字,来保证内存可见性。

完整代码如下:

class  SingleLazy{
    private volatile static SingleLazy instance=null;
    //创建一个实例对象
    public static SingleLazy getInstance() {
        //判断是否需要加锁
        if(instance==null) {
            synchronized (SingleLazy.class) {
                //判断对象是否实例化
                if (instance == null) {
                    instance = new SingleLazy();
                }
            }
        }
        return instance;
    }
    private SingleLazy() {
    }
}
相关文章
|
23天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
27 2
|
4天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
|
12天前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入理解与应用
【10月更文挑战第22天】 在软件开发中,设计模式是解决特定问题的通用解决方案。本文将通过通俗易懂的语言和实例,深入探讨PHP中单例模式的概念、实现方法及其在实际开发中的应用,帮助读者更好地理解和运用这一重要的设计模式。
13 1
|
16天前
|
设计模式 Java 程序员
[Java]23种设计模式
本文介绍了设计模式的概念及其七大原则,强调了设计模式在提高代码重用性、可读性、可扩展性和可靠性方面的作用。文章还简要概述了23种设计模式,并提供了进一步学习的资源链接。
32 0
[Java]23种设计模式
|
28天前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入解析与实践
在PHP开发中,设计模式是提高代码可维护性、扩展性和复用性的关键技术之一。本文将通过探讨单例模式,一种最常用的设计模式,来揭示其在PHP中的应用及优势。单例模式确保一个类仅有一个实例,并提供一个全局访问点。通过实际案例,我们将展示如何在PHP项目中有效实现单例模式,以及如何利用这一模式优化资源配置和管理。无论是PHP初学者还是经验丰富的开发者,都能从本文中获得有价值的见解和技巧,进而提升自己的编程实践。
|
25天前
|
设计模式 安全 Java
C# 一分钟浅谈:设计模式之单例模式
【10月更文挑战第9天】单例模式是软件开发中最常用的设计模式之一,旨在确保一个类只有一个实例,并提供一个全局访问点。本文介绍了单例模式的基本概念、实现方式(包括饿汉式、懒汉式和使用 `Lazy<T>` 的方法)、常见问题(如多线程和序列化问题)及其解决方案,并通过代码示例详细说明了这些内容。希望本文能帮助你在实际开发中更好地应用单例模式,提高代码质量和可维护性。
29 1
|
29天前
|
设计模式 缓存 数据库连接
探索PHP中的设计模式:单例模式的实现与应用
在PHP开发中,设计模式是提高代码可复用性、可维护性和扩展性的重要工具。本文将深入探讨单例模式(Singleton Pattern)的基本概念、在PHP中的实现方式以及实际应用场景。单例模式确保一个类仅有一个实例,并提供全局访问点。通过具体代码示例和详细解释,我们将展示如何在PHP项目中有效利用单例模式来解决实际问题,提升开发效率和应用性能。
|
18天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
22 0
|
21天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
本教程详细讲解了Kotlin中的单例模式实现,包括饿汉式、懒汉式、双重检查锁、静态内部类及枚举类等方法,适合需要深入了解Kotlin单例模式的开发者。快速学习者可参考“简洁”系列教程。
27 0
|
21天前
|
设计模式 存储 数据库连接
Python编程中的设计模式之美:单例模式的妙用与实现###
本文将深入浅出地探讨Python编程中的一种重要设计模式——单例模式。通过生动的比喻、清晰的逻辑和实用的代码示例,让读者轻松理解单例模式的核心概念、应用场景及如何在Python中高效实现。无论是初学者还是有经验的开发者,都能从中获得启发,提升对设计模式的理解和应用能力。 ###
下一篇
无影云桌面