寂然解读设计模式 - 单例模式(上)

简介: 接下来我们来聊聊单例模式的八种实现方式,如下所示> 饿汉式(静态常量) > 饿汉式(静态代码块) > 懒汉式(线程不安全) > 懒汉式(线程安全,同步方法) > 懒汉式(线程安全,同步代码块) > 双重检查 > 静态内部类 > 枚举方式
I walk very slowly, but I never walk backwards 

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


寂然

大家好~,我是寂然,本节课呢,我们正式进入设计模式的学习,第一个要聊的就是我们最常见的,同时也是面试高频的单例模式,那老规矩,我们直接启程吧

五重境界

在此之前,我们闲聊两句,因为有的小伙伴问到一个问题,怎么才算是学会或者说掌握设计模式了呢?那我们一起来聊聊这个话题,会有很多人说其实这个不好去定义,但是如果自己平时写代码,无意识,习惯成自然的用到了设计模式,遇到实际问题,很快就能对比选择,使用合适的设计模式,低耦合,合理的解决掉实际问题,我觉得就是很好的掌握了,网上有人分享了设计模式的五重境界,感觉很贴切,这里也拿来和大家分享下


第 1 层:刚开始学编程不久,听说过什么是设计模式 ,但不了解

第 2 层:有很长时间的编程经验了,自己写了很多代码,其中用到了设计模式,但是自己却不知道

第 3 层:学习过了设计模式,发现自己已经在使用了,并且发现了一些新的模式挺好用的

第 4 层:阅读了很多别人写的源码和框架,其中看到了设计模式,能够领会设计模式的精妙和带来的好处

第 5 层:代码写着写着,自己都没有意识到使用了设计模式,并且熟练的写了出来

问题答疑

同时,在开始之间还要和大家强调一句话,因为很多小伙伴都有说或者脑海中有这样一个疑问:这样就可以啊,为什么非要这么复杂呢,大家要注意,设计模式不仅仅是站在实现功能的角度上,而是立足于整个软件系统的设计,所以大家要学会在学习的过程中,总结和对比思考,可以达到事半功倍的效果

官方定义

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


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

单例模式八种方式

那接下来我们来聊聊单例模式的八种实现方式,如下所示

饿汉式(静态常量)

饿汉式(静态代码块)

懒汉式(线程不安全)

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

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

双重检查

静态内部类

枚举方式

饿汉式(静态常量)

饿汉式之静态常量的写法如下:

一,构造器私有化(防止外部通过new + 构造器的方式创建对象实例)

二,类的内部创建对象(创建final,static的实例对象)

三,对外暴露一个公共的静态方法(通过该方法,返回该类唯一的对象实例)

// 饿汉式(静态变量)
class Singleton{
    //一 构造器私有化
    private Singleton(){

    }
    //类的内部创建对象
    private final static 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,证明是同一个实例对象
    }
}
写法分析

优势

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

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

劣势

在类装载的时候就完成实例化,没有达到 Lazy Loading 的效果,如果从始至终从未使用过这个实例,则会造成内存的浪费

饿汉式(静态代码块)

饿汉式(静态代码块)的方式,是在静态代码块中创建单例对象,示例代码如下图所示

//饿汉式 静态代码块
class Singleton{

    private Singleton(){

    }

    private final static Singleton singleton;

    static {
        singleton = new Singleton();
    }

    public static Singleton getInstance(){

        return singleton;
    }
}

public class Singletondemo02 {

    public static void main(String[] args) {

        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance == instance1);

    }
}
写法分析

这种方式和上面的其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例,优劣势和第一种写法一致,写法可用,但可能会造成内存的浪费

饿汉式是不存在线程安全问题的

懒汉式(线程不安全)

懒汉式写法与饿汉式写法的不同在于,并不是一上来就要创建对象,而是当调用对应的getInstance()方法时,才会进行创建,示例代码如下图所示

// 懒汉式(线程不安全)
class Singleton{

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

    }

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

    //提供一个静态的公有方法,当使用到该方法时,才会去创建 instance
    public static Singleton getInstance(){

        fiif (singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

public class SingletonDemo {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance == instance1);

    }
}
写法分析

优势

起到了 lady loading 的效果,不会造成内存浪费,实例使用到的时候,调用getInstance()方法才会创建

劣势

只能在单线程下使用,多线程会出现线程不安全的问题

试想,如果在多线程下,一个线程进入了 if (singleton == null){ ...} 判断语句块,还没来得及往下执行,另一个线程进行判断,此时实例还未来得及创建,也通过了这个判断语句,这时便会产生多个实例,出现了线程不安全问题

所以,在实际开发中,不建议使用这种方式

懒汉式(同步方法)

针对上面线程不安全的问题,我们在懒汉式的代码中,加入同步处理,第一种就是同步方法,相关代码如下

//懒汉式 同步方法
class Singleton{

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

    }

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

    //对外提供公共的静态方法,加入同步处理
    public static synchronized Singleton getInstance(){

        if (singleton == null){
            singleton = new Singleton();  
        }
        return singleton;
    }
}

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

        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance == instance1);
    }
}
写法分析

首先,这种写法,由于加入了同步处理,解决了线程不安全的问题

但是我们说,这样效率很低,每个线程在想获得类的实例的时候,执行 getInstance() 方法都要进行同步,而其实这个方法只执行一次实例化代码就够了,后面的想获得该类的实例,直接return就好了,方法进行同步,效率很低

所以,在实际开发中,也不推荐使用这种方式

懒汉式(同步代码块)

那既然方法进行同步,效率很低,我们可不可以使用同步代码块的方式呢?大家看如下的示例代码

class Singleton{

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

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

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

        if (singleton == null){

            synchronized (Singleton.class){
                singleton = new Singleton();  
            }

        }
        return singleton;
    }
}
写法分析

这种写法就是,本意是想对第四种实现方式的改进,在创建实例对象的时候,进行同步,那大家思考一下,这样可行嘛?可以保证线程安全嘛


但其实,如果在多线程下,一个线程进入了 if (singleton == null){ ...} 判断语句块,还没来得及往下执行,另一个线程进行判断,此时实例还未来得及创建,也通过了这个判断语句,便会产生多个实例,所以在里面创建实例对象的时候,进行同步没有实际意义,在实际开发中,不能使用这种方式

下节预告

OK,由于篇幅的限制,本节内容就先到这里,下一节,我们接着来聊单例模式的后三种写法,包括双重检查机制,静态内部类,枚举,同时会带大家看下JDK源码中单例模式的应用,以及对单例模式的注意事项进行总结,最后,希望大家在学习的过程中,能够感觉到设计模式的有趣之处,高效而愉快的学习,那我们下期见~

相关文章
|
2月前
|
设计模式 存储 SQL
PHP中的设计模式:单例模式的探索
在PHP开发中,单例模式是一种常用的设计模式,它确保一个类只有一个实例,并提供一个全局访问点。本文将通过一个简单的例子,逐步引导你理解如何在PHP中实现和利用单例模式,以及它在实际项目中的应用价值。
|
6天前
|
设计模式 存储 安全
PHP中的设计模式:单例模式深度解析
在软件开发的广袤天地中,设计模式如同璀璨星辰,指引着程序员们解决复杂问题的方向。其中,单例模式以其独特的魅力,确保了一个类仅有一个实例,并提供了一个访问它的全局访问点,成为众多项目中不可或缺的设计智慧。本文旨在深入探讨PHP中单例模式的实现方式、应用场景及背后的哲理,引导读者思考其在现代软件架构中的重要性与运用策略。
20 5
|
11天前
|
设计模式 安全 Java
Java 编程中的设计模式:单例模式的深度解析
【9月更文挑战第22天】在Java的世界里,单例模式就像是一位老练的舞者,轻盈地穿梭在对象创建的舞台上。它确保了一个类仅有一个实例,并提供全局访问点。这不仅仅是代码优雅的体现,更是资源管理的高手。我们将一起探索单例模式的奥秘,从基础实现到高级应用,再到它与现代Java版本的舞蹈,让我们揭开单例模式的面纱,一探究竟。
23 11
|
7天前
|
设计模式 存储 缓存
PHP中的设计模式:单例模式的深入解析
在PHP开发中,设计模式是提高代码可维护性、扩展性和重用性的关键技术之一。本文将深入探讨PHP中的单例模式,包括其定义、实现方式、应用场景以及优缺点。通过对单例模式的全面剖析,帮助开发者更好地理解和应用这一设计模式,从而编写出更加高效和优雅的PHP代码。
|
6天前
|
设计模式 安全 Java
设计模式--单例模式Singleton
这篇文章详细介绍了单例模式Singleton的八种实现方式,包括饿汉式(静态常量和静态代码块)、懒汉式(线程不安全和线程安全的同步方法、同步代码块)、双重检查、静态内部类和枚举。每种方式都有详细的代码示例和优缺点说明,帮助理解单例模式的应用和选择适合的实现方法。
设计模式--单例模式Singleton
|
9天前
|
设计模式 SQL 安全
PHP中的设计模式:单例模式的深入探索与实践在PHP的编程实践中,设计模式是解决常见软件设计问题的最佳实践。单例模式作为设计模式中的一种,确保一个类只有一个实例,并提供全局访问点,广泛应用于配置管理、日志记录和测试框架等场景。本文将深入探讨单例模式的原理、实现方式及其在PHP中的应用,帮助开发者更好地理解和运用这一设计模式。
在PHP开发中,单例模式通过确保类仅有一个实例并提供一个全局访问点,有效管理和访问共享资源。本文详细介绍了单例模式的概念、PHP实现方式及应用场景,并通过具体代码示例展示如何在PHP中实现单例模式以及如何在实际项目中正确使用它来优化代码结构和性能。
|
11天前
|
设计模式 数据库连接 PHP
PHP中的设计模式:单例模式的深入解析与实践
在PHP开发中,设计模式是提高代码可维护性、扩展性和复用性的关键技术之一。本文将深入探讨单例模式——一种确保类只有一个实例,并提供该实例的全局访问点的设计模式。我们将从单例模式的基本概念入手,剖析其在PHP中的应用方式,并通过实际案例展示如何在不同场景下有效利用单例模式来优化应用架构。
|
2月前
|
设计模式 存储 负载均衡
【五】设计模式~~~创建型模式~~~单例模式(Java)
文章详细介绍了单例模式(Singleton Pattern),这是一种确保一个类只有一个实例,并提供全局访问点的设计模式。文中通过Windows任务管理器的例子阐述了单例模式的动机,解释了如何通过私有构造函数、静态私有成员变量和公有静态方法实现单例模式。接着,通过负载均衡器的案例展示了单例模式的应用,并讨论了单例模式的优点、缺点以及适用场景。最后,文章还探讨了饿汉式和懒汉式单例的实现方式及其比较。
【五】设计模式~~~创建型模式~~~单例模式(Java)
|
7天前
|
设计模式 Java 安全
Java设计模式-单例模式(2)
Java设计模式-单例模式(2)
|
2月前
|
设计模式 SQL 缓存
Java编程中的设计模式:单例模式的深入理解与应用
【8月更文挑战第22天】 在Java的世界里,设计模式是构建可维护、可扩展和灵活的软件系统的基石。本文将深入浅出地探讨单例模式这一经典设计模式,揭示其背后的哲学思想,并通过实例演示如何在Java项目中有效运用。无论你是初学者还是资深开发者,这篇文章都将为你打开一扇洞悉软件设计深层逻辑的大门。
28 0