深入理解Java设计模式之单例模式

目录
  • 一、什么是单例模式
  • 二、单例模式的应用场景
  • 三、单例模式的优缺点
  • 四、单例模式的实现
    • 1.饿汉式
    • 2.懒汉式
    • 3.双重加锁机制
    • 4.静态初始化
  • 五、总结

一、什么是单例模式

单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。

许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

单例的实现主要是通过以下两个步骤:

1.将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例;

2.在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。

二、单例模式的应用场景

举一个小例子,在我们的windows桌面上,我们打开了一个回收站,当我们试图再次打开一个新的回收站时,Windows系统并不会为你弹出一个新的回收站窗口。,也就是说在整个系统运行的过程中,系统只维护一个回收站的实例。这就是一个典型的单例模式运用。

继续说回收站,我们在实际使用中并不存在需要同时打开两个回收站窗口的必要性。假如我每次创建回收站时都需要消耗大量的资源,而每个回收站之间资源是共享的,那么在没有必要多次重复创建该实例的情况下,创建了多个实例,这样做就会给系统造成不必要的负担,造成资源浪费。

再举一个例子,网站的计数器,一般也是采用单例模式实现,如果你存在多个计数器,每一个用户的访问都刷新计数器的值,这样的话你的实计数的值是难以同步的。但是如果采用单例模式实现就不会存在这样的问题,而且还可以避免线程安全问题。同样多线程的线程池的设计一般也是采用单例模式,这是由于线程池需要方便对池中的线程进行控制

同样,对于一些应用程序的日志应用,或者web开发中读取配置文件都适合使用单例模式,如HttpApplication 就是单例的典型应用。

从上述的例子中我们可以总结出适合使用单例模式的场景和优缺点:

适用场景:

1.需要生成唯一序列的环境

2.需要频繁实例化然后销毁的对象。

3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。

4.方便资源相互通信的环境

三、单例模式的优缺点

优点:

  • 在内存中只有一个对象,节省内存空间;
  • 避免频繁的创建销毁对象,可以提高性能;
  • 避免对共享资源的多重占用,简化访问;
  • 为整个系统提供一个全局访问点。

缺点:

  • 不适用于变化频繁的对象;
  • 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;
  • 如果实例化的对象长时间不被利用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失;

四、单例模式的实现

1.饿汉式

// 饿汉式单例
public class Singleton1 {
     // 指向自己实例的私有静态引用,主动创建
    private static Singleton1 singleton1 = new Singleton1();
     // 私有的构造方法
    private Singleton1(){}
     // 以自己实例为返回值的静态的公有方法,静态工厂方法
    public static Singleton1 getSingleton1(){
        return singleton1;
    }
}

我们知道,类加载的方式是按需加载,且加载一次。。因此,在上述单例类被加载时,就会实例化一个对象并交给自己的引用,供系统使用;而且,由于这个类在整个生命周期中只会被加载一次,因此只会创建一个实例,即能够充分保证单例。

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

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

2.懒汉式

// 懒汉式单例
public class Singleton2 {
     // 指向自己实例的私有静态引用
    private static Singleton2 singleton2;
     // 私有的构造方法
    private Singleton2(){}
     // 以自己实例为返回值的静态的公有方法,静态工厂方法
    public static Singleton2 getSingleton2(){
        // 被动创建,在真正需要使用时才去创建
        if (singleton2 == null) {
            singleton2 = new Singleton2();
        }
        return singleton2;
    }
}

我们从懒汉式单例可以看到,单例实例被延迟加载,即只有在真正使用的时候才会实例化一个对象并交给自己的引用。

这种写法起到了Lazy Loading的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。

3.双重加锁机制

public class Singleton
    {
        private static Singleton instance;
        //程序运行时创建一个静态只读的进程辅助对象
        private static readonly object syncRoot = new object();
        private Singleton() { }
        public static Singleton GetInstance()
        {
            //先判断是否存在,不存在再加锁处理
            if (instance == null)
            {
                //在同一个时刻加了锁的那部分程序只有一个线程可以进入
                lock (syncRoot)
                {
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

Double-Check概念对于多线程开发者来说不会陌生,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以保证线程安全了。这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null),直接return实例化对象。

使用双重检测同步延迟加载去创建单例的做法是一个非常优秀的做法,其不但保证了单例,而且切实提高了程序运行效率

优点:线程安全;延迟加载;效率较高。

4.静态初始化

//阻止发生派生,而派生可能会增加实例
    public sealed class Singleton
    {
        //在第一次引用类的任何成员时创建实例,公共语言运行库负责处理变量初始化
        private static readonly Singleton instance=new Singleton();
         private Singleton() { }
        public static Singleton GetInstance()
        {
            return instance;
        }
    }

在实际应用中,C#与公共语言运行库也提供了一种“静态初始化”方法,这种方法不需要开发人员显式地编写线程安全代码,即可解决多线程环境下它是不安全的问题。

五、总结

当然,单例模式的实现方法还有很多。但是,这四种是比较经典的实现,也是我们应该掌握的几种实现方式。

从这四种实现中,我们可以总结出,要想实现效率高的线程安全的单例,我们必须注意以下两点:

尽量减少同步块的作用域;

尽量使用细粒度的锁。

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • java设计模式-单例模式实现方法详解

    目录 饿汉式 静态变量 静态代码块 懒汉式 线程不安全 线程安全 双重检查 静态内部类 总结 a 饿汉式 所谓饿汉式,就是直接创建出类的实例化,然后用private私有化,对外只用静态方法暴露. 静态变量 步骤 构造器私有化 类的内部创建对象 向外暴露一个静态的公共方法 优点 缺点 写法简单,在类装载的时完成实例化,避免了线程同步问题 类装载时完成实例化,没有达到LazyLoading的效果,若该实例从未使用,则会造成内存浪费 class Singleton { //私有化构造器 private

  • Java设计模式系列之深入浅出单例模式

    目录 前言 饿汉式 懒汉式 线程安全问题 volatile的作用 总结 前言 我不知道大家工作或者面试时候遇到过单例模式没,面试的话我记得我当时在17年第一次实习的时候,就遇到了单例模式,面试官是我后来的leader,当时就让我手写单例,我记得我就写出了饿汉式,懒汉式,但是并没说出懒汉和饿汉的区别,当时他给我一通解释我才知道了其中的奥秘. 写这篇文章之前我刻意的在我手上的项目里面去找了找,我发现单例在每个项目里面都有运用到,而且我后面所说的几种实现还基本上都涉及了,还挺有意思的. 开篇我就给大家

  • Java设计模式之单例模式深入探索

    目录 什么是设计模式? 单例模式是什么? 单例模式设计的原则是什么? Java实现单例模式的5种方式? 懒汉 饿汉 静态内部类 双重校验锁DCL(Double Check Lock) 枚举(num) 小结 ❤️‍您好,我是贾斯汀,今天来聊一聊单例模式!❤️‍ 什么是设计模式? 百科: 设计模式是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结. 设计模式是软件行业的通用的设计标准,在Java同样通用,主要有23种设计模式如下: 有的小伙伴可能会问,这么多,学得完吗? 答:不好意思

  • JAVA设计模式---单例模式你知道吗

    目录 单例模式的介绍 单例模式实现的八种方式 饿汉式 静态常量 静态代码块 懒汉式 线程不安全的写法 线程安全,同步锁-效率低,不推荐 线程安全,同步代码块-无法解决线程安全问题,不推荐 双重检查-解决线程安全和懒加载问题–推荐使用 静态内部类-可以实现懒加载,线程安全,推荐使用 枚举 单例模式注意事项 总结 单例模式的介绍 单例模式实现的八种方式 饿汉式 静态常量 步骤: 1.构造器私有化(防止new) 2.类的内部创建对象 3.向外暴露一个静态的公共方法-getInstance //饿汉式静

  • JAVA设计模式零基础解析之单例模式的八种方式

    目录 单例模式简介: 单例模式优点: 应用场景: 单例设计模式的八种方式: 1.饿汉式(静态常量) 2.饿汉式(静态代码块) 3.懒汉式(线程不安全) 4.懒汉式(线程安全,同步方法) 5.懒汉式(线程安全,同步代码块) 6.双重检查(推荐使用) 7.静态内部类(推荐使用) 8.枚举(推荐使用) 单例模式在JDK应用的源码分析 单例模式注意事项和细节说明 单例模式简介: 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了

  • 深入理解Java设计模式之单例模式

    目录 一.什么是单例模式 二.单例模式的应用场景 三.单例模式的优缺点 四.单例模式的实现 1.饿汉式 2.懒汉式 3.双重加锁机制 4.静态初始化 五.总结 一.什么是单例模式 单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在. 许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为.比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息.这种方式简

  • java设计模式之单例模式的详解及优点

    java设计模式之单例模式 定义:如果一个类始终只能创建一个实例,那么这个类被称为单例类,这种设计模式被称为单例模式. Spring框架里面可以将所有生成的bean对象都设置为单例模式,只需要在配置Bean实例时指定scope="singleton"即可,或者不做配置默认即为单例模式. 我们可以创建一个小的Demo来演示单例模式的实现,只需要保证该类只能创建一个实例,我们可以用权限修饰符private修饰该类的构造器. 提供一个创建该类的接口,该接口只能用static修饰,类里面创建一

  • Java设计模式之单例模式实例分析

    本文实例讲述了Java设计模式之单例模式.分享给大家供大家参考,具体如下: 单例模式:(Singleton Pattern)是一个比较简单的模式,其定义如下: Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例) 单例模式,很简单的一个模式.其实在android开发中,很多地方都会用到单例模式,比如某些工具类.Json数

  • Java设计模式之单例模式实例详解【懒汉式与饿汉式】

    本文实例讲述了Java设计模式之单例模式.分享给大家供大家参考,具体如下: 单例模式就是产生一个对象实例,供外外部访问. 它的应用场景就是在这个类在全局真资源需要统一访问,否则会造成混乱时,才有必要设计成单例. 懒汉式,就是在使用这个对象时,才去查看这个对象是否创建,如果没创建就马上创建,如果已经创建,就返回这个实例. 饿汉式,在加载这个类的时候就先创建好一个对象实例,等待调用. 两者的优缺点也能猜到,使用懒汉式,在反应速度上肯定要比饿汉式慢. 但是这个对象如果不被调用,那就节省了cpu和内存资

  • java 设计模式之单例模式

    java  设计模式之单例模式 前言: 在软件开发过程中常会有一些对象我们只需要一个,如:线程池(threadpool).缓存(cache).对话框.偏好设置等.这些对象如果制造出多个实例的话可能会导致一些不必要的麻烦,如:程序行为异常.资源使用过量等.这时单例模式就可以确保一个类只有一个实例,并提供全局访问点.下面是从简单的单例类来探讨该用何种方法实现单例模式. /** * 最经典的单例类 */ public class Singleton { // 设置成静态变量来记录Singleton的唯

  • 快速理解Java设计模式中的组合模式

    组合模式是一种常见的设计模式(但我感觉有点复杂)也叫合成模式,有时又叫做部分-整体模式,主要是用来描述部分与整体的关系. 个人理解:组合模式就是将部分组装成整体. 定义如下: 将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性. 通用类图如下: 组合模式的包含角色: ● Component 抽象构件角色 定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性. ● Leaf 叶子构件 叶子对象,其下再也没有其他的分支,也就是遍历的最

  • 全面解析Java设计模式之单例模式

    本文实例为大家分享了Java设计模式之单例模式的具体代码,供大家参考,具体内容如下 概念: 单例模式:一个类中只有一个实例. 一个类有且仅有一个实例,并且提供了一个全局的访问点. 使用该模式的起因: 当我们在浏览网站时,有些网站会显示"当前在线人数".通常,实现这个功能的办法是将登陆的每一个IP存储在一个内存.文件或者数据库中,每多一个IP,就实现"+1".一般就是用一个方法,比如add(),实现"+1"的功能,比如用"update&q

  • Java设计模式之单例模式简介

    一.饿汉式(静态常量) public class Face { private static final Face instance = new Face(); static Face getInstance() { return instance; } private Face() { } } 优点:这种写法比较简单,就是在类装载的时候就完成实例化.避免了线程同步问题. 缺点:在类装载的时候就完成实例化,没有懒加载的效果.如果从始至终从未使用过这个实例,则会造成内存的浪费. 二.饿汉式(静态代

  • Java设计模式之单例模式示例详解

    目录 0.概述 1.饿汉式 1.1 饿汉式单例实现 1.2 破坏单例的几种情况 1.3 预防单例的破坏 2.枚举饿汉式 2.1 枚举单例实现 2.2 破坏单例 3.懒汉式 4.双检锁懒汉式 5.内部类懒汉式 6.JDK中单例的体现 0.概述 为什么要使用单例模式? 在我们的系统中,有一些对象其实我们只需要一个,比如说:线程池.缓存.对话框.注册表.日志对象.充当打印机.显卡等设备驱动程序的对象.事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的产生,比如:程序的行为异常.

随机推荐