专栏首页bigsai设计模式—单例模式

设计模式—单例模式

前言

好久没写东西了,但是想着无论什么事还是要坚持自己初心要坚持的东西。写东西不能断!

对于常用的23种设计模式,这里笔者会根据自己学习和出现频率、重要程度进行学习记录吧。并且每种设计模式可能会根据暂时需求侧重学习深浅。

单例模式

有很多直接把单例分成很多种,这里我就分两个大类(饿汉懒汉)在这里说啦。

单例模式(Singleton Pattern)是设计模式中最简单的模式之一,属于 创建型模式。这种设计模式主要是类的对象只有一个实例,不需要每次new 创造。而我们要做的的就是确保这个对象创建的 唯一。然后根据一些特征进行优化创建以及访问改类。

而单例模式也有很多的应用,比如很多驱动例如摄像头、打印机等等,而在javaweb中的spring有很多配置文件,掌控全局,同样也是单例的。

对于单例,主要是全局只有这么一个对象。对了它的理解,这里笔者打几个有可能不太恰当的理解,目的在于帮助理解记忆,如果有错误还请指正。

个人可能不太恰当的理解

  • 一定程度上单例模式和普通的模式有可能是一根根火柴打火机的差别。火柴想燃烧每次都要一根火柴摩擦燃烧为灰烬作为代价。而一个打火机可以持续供给。同样如果你只想在某个线程某个类想要得到执行改类的一个方法,如果这个类占用的内存、空间巨大,耗费的时间也很大的话,如果频繁创造是一笔很大的负担,那样就不如咱们就创建好一个然后供我们使用就好啦。
  • 一定程度又像是他人和个人的区别。每个人都可能干过相同的事情,可能很多人都在校园的跑道上跑过步,但他们并不是你,你也可能在操场上跑过很多圈。但只有你知道你的思考。同样单例模式在一定程度可以看作一个人生,它可能有很多职能,也可能干过很多事、重复过很多事。但它可能对自己有从始至终的思考和记录。它可能有些全局参数记录着程序从始至终一些状态、信息等等。也就是它一直是它,你也一直是你独特的自己
  • 其他等等

至于单例模式的优缺点,这里就不作详细介绍了。无非是关于 性能职能时间空间拓展性等方面的一些讨论。这个可以参考不同人的不同理解。本文主要记录单例模式的实现方面。而同样单例模式实现上分为饿汉式懒汉式

单例模式创建要求

  • 某个类只能有一个实例(构造器私有化)
  • 它必须自行创建这个实例(含有一个改类的静态变量来保存这个唯一的实例)
  • 自行向整个系统提供这个实例( 直接暴露或者用 静态变量的get方法)

饿汉式

直接创建对象,不存在线程安全问题 。对于饿汉式的实现是相对简单和容易的。在实际遇到这种类似的思想其实也很多。理解:

  • 这个饿汉式就好比双十一,你一下把你家里感觉可能缺的日后可能需要(也不一定需要的)全部给买了。比如啥洗衣液屯、棉衣棉鞋棉被日后可能要买也买。然后啥微波炉、烤箱我 感觉可能以后会用我也买买买。买买买。这一下就买全了。但是:
  • 同样在实际的生产,我们一个项目中可能有很多这样单例的存在,如果一次性启动的话对时间花销真的是太大。可能对程序的压力和体验都很差。所以一般很少直接使用。这种体验,像极了你打开某个网页等待JavaScript和css的过程。

饿汉式的几种实现方式

01 . 直接实例化饿汉式(简洁直观)

/**
 * 饿汉式
 * 直接创建实例,不管是否需要这个对象
 */
publicclass singleton1 {
publicstaticfinal singleton1 INSTANCE = new singleton1();
private singleton1()
{}
}

02 . 枚举式(最简洁)

publicenum singleton2 {
   INSTANCE
}

03 . 静态代码块饿汉式(适合复杂实例化)

publicclass singleton3 {
publicstaticfinal singleton3 INSTANCE;
static{
/***
         * 有可能一些数据需要配置,需要从类加载器加载
         * 例如从某xxx.properties加载某些配置信息,我们只需更改配置文件不需要更改代码
         */
        INSTANCE=new singleton3();
}
private  singleton3()
{}
}

懒汉式

我们知道饿汉式在早早把对象创建好,在执行一些其他逻辑时候不存在线程安全的问题。但是懒汉式就不一样啦,延迟创建对象。

01 .线程不安全(适合单线程)

/***
 * 懒汉式:构造器私有化
 * 静态变量保存实例
 * 提供一个静态方法,获取这个实例对象
 */
publicclass singleton4 {
privatestatic singleton4 instance;
private  singleton4(){}
publicstatic singleton4 getInstance()
{
if(instance==null)
            instance = new singleton4();
return  instance;
}
}

对于这种单线程没问题,但是如果如果两个或多个线程来同时访问instance如果都为null同时申请的时候会遇到下图等之类问题,这违背了单例模式的设计原则并且可能会对系统数据造成错误。

02 .线程安全(适用于多线程)怎么优化上述的懒汉式单例模式呢?既然不允许多个线程这样同时访问,那么咱们给它上个锁不久行了嘛!

publicclass singleton5 {
privatestatic singleton5 instance;
private  singleton5(){}
publicstatic singleton5 getInstance()
{
synchronized(singleton5.class)
{
if(instance == null)
                instance = new singleton5();
}
return  instance;
}
}

但是这样有啥问题呢?就是我获取这个单例对象的时候,被上锁了。你气不气?我们就是在创建这个单例时候多线程可能出现点问题,咱么获取的时候不存在线程安全问题啊。。你为啥获取也要锁??

这就好比,咱们都喜欢看美女,跟美女说话要排队一个一个来,只能一个在一个时刻,但是你看美女不需要排队啊!!!

03 .线程安全推荐版(适用于多线程)对于上述存在的问题,咱们只需要双重判定就行了。

  • 首先判断instance是不是为null。如果不是null说明被实例化过直接返回即可!nice!
  • 如果instance为null?上锁之后再判空实例化。
publicclass singleton6 {
privatestatic singleton6 instance;
private singleton6(){}
publicstatic singleton6 getInstance()
{
if(instance==null) {
synchronized(singleton6.class) {
if(instance == null)
                    instance = new singleton6();
}
}
return instance;
}
}

两个判断为null?为啥要两个?

  • 第一个:用来判断是否为空需要上锁new 实例。
  • 第二个:上锁后虽然只有一个会执行当前方法,但是很可能都为null的时候两个或多个都为null上锁的想构造。然后后面线程在等待同时前面线程构造好了,那么它就不需要再去构造这个 instance啦!直接不操作就行了。

就这样,稍微完美的方法就这样产生了。

04 .静态内部类形式(适用于多线程)

上面的方法可能有些复杂,而静态内部类也是个好方式。主要是静态内部类和外部类不是一起加载的,并且你去调用它的时候他就会初始化,并且类加载是线程安全的,这个不需要考虑线程安全问题。当加载完之后你就可以直接拿啦。这样也能达到延迟加载的作用。

这个更详细你可以自己研究静态变量(类变量)、静态内部类等等加载顺序,研究下`static关键字。

publicclass singleton7 {
private singleton7(){}
privatestaticclass inner
{
privatestaticfinal singleton7 instance=new singleton7();
}
publicstatic singleton7 getInstance()
{
return inner.instance;
}
}

结语

学习参考尚学堂单例讲解以及百科、菜鸟教程等等,有些区别但是大部分实现都是相似的,带上个人理解分享给大家。如果有问题和疏漏还请指教!

本文分享自微信公众号 - bigsai(bigsai),作者:bigsai

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-03-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 再不怕和老外聊天了!我用python写了个微信聊天翻译助手!

    在前面的一篇文章如何用python“优雅”的调用有道翻译?中咱们清楚的写过如何一层一层的解开有道翻译的面纱,并且笔者说过那只是脑洞的开始。现在笔者又回来了。Te...

    bigsai
  • 剑指offer(16-30题) 精解

    比如两个链表你可以用一个list1作为主链表返回。返回另一个list2进行遍历比较插入到主链表适当位置中。有兴趣可以试一试。 当然你还可以直接建立一个新链表头节...

    bigsai
  • 写文没高质量配图?教你python爬虫绕过限制一键搜索下载图虫创意图片!

    在我们写文章(博客、公众号、自媒体)的时候,常常觉得自己的文章有些老土,这很大程度是因为配图没有选好。本文将和大家分享一个实用爬虫案例!

    bigsai
  • Hi,我们再来聊一聊Java的单例吧

    原文:http://www.barryzhang.com/archives/521

    好好学java
  • Hi,我们再来聊一聊Java的单例吧

    单例(Singleton)应该是开发者们最熟悉的设计模式了,并且好像也是最容易实现的——基本上每个开发者都能够随手写出——但是,真的是这样吗? 作为一个Java...

    Java团长
  • 【Java学习笔记之三十】详解Java单例(Singleton)模式

    概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。   单例模式有以下特点: 1...

    Angel_Kitty
  • 02、人人都会设计模式--单例模式

    一个男人只能有一个媳妇「正常情况」,一个人只能有一张嘴,通常一个公司只有一个 CEO ,一个狼群中只有一个狼王等等

    TigerChain
  • 日常理解

    { 空 } 1. 什么叫线程安全?servlet是线程安全吗? { 答:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次...

    江湖前辈黄药师
  • 为什么相比于RNN,LSTM在梯度消失上表现更好

    对于深度学习模型,在train参数的时候,需要采用随机梯度下降方法(SGD,Stochastic Gradient Descent):

    三猫
  • 一男子给对象转账5000元,居然又退还了!

    在并发编程中,所有问题的根源就是可见性、原子性和有序性问题,这篇文章我们就来聊聊原子性问题。

    武培轩

扫码关注云+社区

领取腾讯云代金券