23种设计模式之单例模式(懒汉,饿汉,线程安全懒汉)

23种设计模式之单例模式

    • 概要
    • 设计思想
    • 单例模式的优缺点
    • 要点
    • 饿汉型单例模式
    • 懒汉型单例模式
    • 懒汉线程安全型单例模式
    • 小结

概要

我们知道设计模式分为23种但是具体划分的话,又分为三大类①:创建型②:结构型③:行为型,本文会介绍创建型的单例模式希望各位能够简单的去了解单例模式以及能够在正常的开发中得到运用,单例模式常见的有饿汉型单例模式、懒汉型单例模式、懒汉线程安全型单例模式,实际上单例模式有七种左右,本文仅介绍常见的三种。

设计思想

原文:单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这个类通常会提供一个构造函数,用于创建该实例,同时也会提供一个静态方法,用于获取该实例。单例模式通常用于创建系统级别的对象,如日志记录器、配置管理器等。它能够确保系统中只有一个实例,并提供一个全局访问点,方便其他对象使用该实例。

单例模式的优缺点

单例模式的优点

  • 实例控制:单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
  • 节约系统资源:由于在系统内存中只存在一个对象,因此可以节约系统资源。当需要频繁创建和销毁的对象时,单例模式无疑可以提高系统的性能。
  • 避免对共享资源的多重占用。
  • 灵活性:由于类控制了实例化过程,所以类可以灵活更改实例化过程。

单例模式的缺点

  • 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
  • 单例类的职责过重,在一定程度上违背了“单一职责原则”。
  • 如果滥用单例,将带来一些负面问题。例如,为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

要点

显然单例模式的要点有三个;一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
从具体实现角度来说,就是以下三点:一是单例模式的类只提供私有的构造函数,二是类定义中含有一个该类的静态私有对象,三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。
在对象图中,有一个"单例对象",而"客户甲"、“客户乙” 和"客户丙"是单例对象的三个客户对象。可以看到,所有的客户对象共享一个单例对象。而且从单例对象到自身的连接线可以看出,单例对象持有对自己的引用。
一些资源管理器常常设计成单例模式。
在计算机系统中,需要管理的资源包括软件外部资源,譬如每台计算机可以有若干个打印机,但只能有一个Printer Spooler, 以避免两个打印作业同时输出到打印机中。每台计算机可以有若干传真卡,但是只应该有一个软件负责管理传真卡,以避免出现两份传真作业同时传到传真卡中的情况。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。
需要管理的资源包括软件内部资源,譬如,大多数的软件都有一个(甚至多个)属性(properties)文件存放系统配置。这样的系统应当由一个对象来管理一个属性文件。
需要管理的软件内部资源也包括譬如负责记录网站来访人数的部件,记录软件系统内部事件、出错信息的部件,或是对系统的表现进行检查的部件等。这些部件都必须集中管理,不可整出多头。
这些资源管理器构件必须只有一个实例,这是其一;它们必须自行初始化,这是其二;允许整个系统访问自己这是其三。因此,它们都满足单例模式的条件,是单例模式的应用。

饿汉型单例模式

/**
 * 饿汉 单例模式
 */
public class Hungry {
    /**
     * 懒汉时提到过  static 保证对象的唯一性
     * 静态成员 无需通过  实例引用 就可以调用
     * 在类的加载时已经生成目标单例对象  类只会加载一次 
     * 这就表明饿汉模式天生就是线程安全的
     */
    private static Hungry hungry = new Hungry();

    private Hungry() {
    }

    private Hungry(Hungry hungry) {
    }

    /**
     * 饿汉 单例 无延迟
     */
    public static Hungry getInstance() {
        return hungry;
    }

懒汉型单例模式

/**
 * 懒汉模式
 */
public class Slacker {
    /**
     * 懒汉 单例模式中  获取对象是懒加载的
     * 并不会因为我们调用对应的get方法  就会获取实例对象
     * static 修饰  保证成员变量的唯一性 该数据存储在 jvm内存模型中的方法区中
     * 在单例类中声明一个静态的目标单例类的 成员变量
     */
    private static Slacker slacker;

    /**
     * 私有化 目标单例类的 构造方法
     */
    private Slacker() {
    }

    private Slacker(Slacker slacker) {
    }

    /**
     * 调用方法获取 目标单例类的实例
     */
    public static Slacker getInstance() {
        /**
         * 懒汉判断 目标单例是否已经生成对象
         * 懒加载操作
         * 该if 可能会存在多线程  同时判断得出  目标单例为空  所以多个线程进入if 条件
         * 生成多次目标单例类
         */
        if (slacker == null) {
            slacker = new Slacker();
        }
        return slacker;
    }
}

懒汉线程安全型单例模式

/**
 * 懒汉 多重检查  单例模式
 */
public class DuplicationCheck {
    private static DuplicationCheck duplicationCheck;

    private DuplicationCheck() {
    }

    private DuplicationCheck(DuplicationCheck duplicationCheck) {
        this.duplicationCheck = duplicationCheck;
    }

    public static DuplicationCheck getInstance() {
  		//当目标单例已经生成对象  减少线程去获取锁资源
        if (duplicationCheck == null) {
            synchronized (DuplicationCheck.class) {
            //第一个if条件  可能在多线程中出现多个线程判断目标单例为空 当第一个线程拿
            //到锁资源之后生成单例对象然后返回  但是  第二条线程以及  第三条线程  已进
            //入第一个if 了  导致拿到锁资源后再次生成目标单例对象 所以  在  同步代码块中
            //又进行了  目标单例类的非空判断
                if (duplicationCheck == null) {
                    duplicationCheck = new DuplicationCheck();
                }
            }
        }
        return duplicationCheck;
    }

}

小结

在三种类型中 展示了 线程安全的单例以及线程不安全的单例 懒汉线程不安全只能 在后期添加同步代码块才能在正常开发中使用懒汉模式 当然 大多数情况下
单例模式 并不需要我们去写 比较现在又很多框架(Spring) 能满足我们的需求代码已经贴出来了 其中也有我自认为比较详细的注释 希望能够成为大家在学习设计模式中的一大助力

你可能感兴趣的:(设计模式,java)