当前位置:首页 » 《休闲阅读》 » 正文

Java中的单例模式(如果想知道Java中有关单例模式的知识,那么只看这一篇就足够了!)

25 人参与  2024年11月13日 08:01  分类 : 《休闲阅读》  评论

点击全文阅读


        前言:单例模式(Singleton Pattern)是一种确保类仅有一个实例,并提供全局访问点的设计模式,它常用于数据库连接、日志管理等需要共享资源的场景,通过避免重复创建实例,提高资源利用率。


✨✨✨这里是秋刀鱼不做梦的BLOG

✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客

在正式开始讲解之前,先让我们看一下本文大致的讲解内容:

目录

1.单例模式简介和应用场景

        (1)单例模式的简介

        (2)单例模式的应用场景

2.饿汉模式

3.懒汉模式 - 单线程版

4.懒汉模式 - 多线程版

5.懒汉模式 - 多线程版(改进)


1.单例模式简介和应用场景

        (1)单例模式的简介

        在开始学习如何使用单例模式之前,先让我们了解一下什么是单例模式:

        单例模式(Singleton Pattern)是一种创建型设计模式,它确保在应用程序的整个生命周期中,某个类只有一个实例存在,并提供一个全局的访问点来使用这个实例。

        想象我们要建立一个管理系统,用于控制系统资源(比如数据库连接、文件读写等),如果每个操作都创建新的实例,将导致资源的浪费,而单例模式的出现正是为了节约资源,并确保同一时间内应用中只存在一个对象实例。

        (2)单例模式的应用场景

        在了解完了什么是单例模式之后,再让我们了解一下单例模式的应用场景吧,以下为单例模式的应用场景:

数据库连接池:只需一个连接池对象,重复使用已存在的数据库连接。

配置文件读取:配置文件一般是全局的,所以只需要一个配置文件读取对象。

线程池:为了高效管理线程,确保线程资源的有效利用。

日志类:使用单例确保全局只有一个日志记录器,从而统一管理日志输出。

        至此,我们就大致的了解了单例模式是什么和有什么用处了!!!

        常见的单例模式实现包括饿汉模式懒汉模式,前者实例初始化较早,后者实例初始化较晚,但使用了“延迟加载”机制,两种方式在性能、安全性等方面各有优缺点,那么接下来就让我们一一进行讲解。

2.饿汉模式

        在学习如何使用饿汉模式之前,先让我们了解一下什么是饿汉模式。

原理及实现:

        饿汉模式在类加载时就创建实例,无论程序是否需要,实例都会被提前初始化。此模式的核心是“在类加载时创建实例”,也因此得名“饿汉”模式。由于类加载只执行一次,饿汉模式天然是线程安全的,不会引发并发问题。

饿汉模式代码示例:

public class Singleton {    // 静态成员变量,类加载时创建单例实例    private static final Singleton instance = new Singleton();    // 私有化构造方法,防止外部创建对象    private Singleton() {}    // 静态方法,提供全局访问单例实例的入口    public static Singleton getInstance() {        return instance;    }}

这里我们对上述的代码进行分析讲解,以便于读者更好的理解:

代码分析:

instance 是一个静态变量,属于 Singleton 类,并在类加载时初始化。将构造方法 Singleton() 设置为私有,避免外部通过构造方法创建对象。getInstance 方法为用户提供全局访问点,始终返回同一个实例。

优缺点分析:

优点:实现简单,类加载时自动创建实例,天然具备线程安全性。缺点:即使实例未被使用,仍会被创建,浪费内存资源,不适合大对象的延迟加载需求。

        通过上述的讲解,我们可以了解到饿汉模式适合对资源消耗不高的情况,或者单例对象必然会被使用的场景,这样我们就大致的了解了什么是饿汉模式并且会使用它了。

3.懒汉模式 - 单线程版

        了解完了饿汉模式之后,在让我们看一下懒汉模式,首先先让我们讲解一下懒汉模式 - 单线程版:

原理及实现:

        懒汉模式采用“延迟加载”的方式,即实例只在第一次使用时才进行创建,而不是类加载时初始化。

        这种实现方式的好处是仅在需要时才创建实例,从而节省资源,但在单线程的场景中,这种方式没有问题,但在多线程环境下可能导致多个线程同时创建实例,违背了单例模式的初衷。

懒汉模式 - 单线程版代码代码示例:

public class Singleton {    // 静态成员变量,初始化为 null    private static Singleton instance = null;    // 私有化构造方法,防止外部创建对象    private Singleton() {}    // 静态方法,在首次调用时创建实例    public static Singleton getInstance() {        if (instance == null) {            instance = new Singleton(); // 只有首次调用才会创建实例        }        return instance;    }}

和上述的饿汉模式一样,这里我们对上述的代码进行分析讲解,以便于读者更好的理解:

代码分析:

instance 被初始化为 null,意味着在首次调用 getInstance 方法之前不会创建实例。getInstance 方法在 instancenull 时创建单例对象。

优缺点分析:

优点:支持延迟加载,仅在首次使用时才初始化,节省内存。缺点:无法应对多线程并发场景,多个线程同时访问 getInstance 时可能创建多个实例,违背单例的核心思想。

        通过上述的讲解,我们可以了解到单线程懒汉模式仅适用于单线程环境或不涉及并发访问的单例需求,适用于较小的单线程应用,这样我们就大致的了解了懒汉模式 - 单线程版啦。

4.懒汉模式 - 多线程版

        了解完懒汉模式 - 单线程版之后,让我们学习一下懒汉模式 - 多线程版:

原理及实现:

        为了解决懒汉模式在多线程环境下的并发问题,可以在 getInstance 方法上添加 synchronized 关键字,确保同一时间只有一个线程可以调用 getInstance 方法。

虽然这种方式解决了线程安全问题,但同步操作会带来性能开销,特别是在高并发环境下

懒汉模式 - 多线程版代码代码示例:

public class Singleton {    private static Singleton instance = null;    private Singleton() {}    // synchronized 保证线程安全,但会带来性能开销    public static synchronized Singleton getInstance() {        if (instance == null) {            instance = new Singleton(); // 仅首次调用时创建实例        }        return instance;    }}

这里我们还是对上述的代码进行分析讲解,以便于读者更好的理解:

代码分析:

synchronized 关键字保证 getInstance 方法的同步性,避免多个线程同时调用时创建多个实例。每次调用 getInstance 都会锁定方法,保证线程安全,但影响了性能。

优缺点分析:

优点:线程安全,确保在多线程环境下只会创建一个实例。缺点:同步带来的性能开销,使得每次调用 getInstance 都需要获取锁,性能较低。

        而这种方式适合低并发场景,不建议用于频繁访问的单例模式实现。

5.懒汉模式 - 多线程版(改进)

        在上述的懒汉模式 - 多线程版中,其实隐藏着许许多多的问题,这里我们对上述隐藏的问题进行一一解决:

改进一:

        为提高性能,我们可以采用双重检查锁定(Double-Check Locking, DCL)来减少同步开销。双重检查锁定的思路是:在同步代码块的外部添加一个 if 判断,以减少不必要的同步操作。通过双重检查锁定,可以确保只在首次创建实例时进行同步,后续调用直接返回已有实例,避免性能下降。

改进二:

        为了保证内存的可见性并防止指令重排序,可以在 instance 前加上 volatile 关键字。

改进步骤:

        第一步:添加外层 if 判断 首先在 synchronized 代码块外添加 if 判断,减少不必要的同步操作。

public static Singleton getInstance() {    if (instance == null) { // 第一次检查        synchronized (Singleton.class) {            if (instance == null) { // 第二次检查                instance = new Singleton();            }        }    }    return instance;}

        第二步:引入 volatile 关键字 添加 volatile 关键字来确保实例在不同线程下的内存可见性,避免因指令重排序导致的问题。

public class Singleton {    private static volatile Singleton instance = null; // 确保多线程间的可见性    private Singleton() {}    public static Singleton getInstance() {        if (instance == null) { // 第一次检查            synchronized (Singleton.class) {                if (instance == null) { // 第二次检查                    instance = new Singleton();                }            }        }        return instance;    }}

        同样我们对上述的代码进行分析讲解,以便于读者更好的理解:

代码分析:

首次调用时,外层的 if 判断 instance == null,避免了每次调用都进行同步操作,提高了性能。在同步代码块内部进行二次检查,确保在多线程并发的情况下也不会重复创建实例。volatile 关键字避免了指令重排序问题,确保了多个线程能够正确地读取到最新的 instance 值。

优缺点分析:

优点:线程安全,支持高并发场景,并减少了不必要的同步操作,提高性能。缺点:相较于饿汉模式,代码实现更为复杂,增加了理解和维护难度。

        通过上述的改进之后,使得懒汉模式适应高并发场景,特别是实例初始化资源较大的情况。

——至此我们就了解了所以有关单例模式的内容了!!!


以上就是本篇文章的全部内容了~~~


点击全文阅读


本文链接:http://zhangshiyu.com/post/185361.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1