设计模式(4)——单例模式的学习及其六大战将

13 篇文章 1 订阅
1 篇文章 0 订阅
本文回忆了大学时期学习单例模式的经历,介绍了单例模式的基本概念,并揭示了单例模式的六种实现方式:懒汉式(线程不安全)、懒汉式(线程安全)、饿汉模式、双检锁/双重校验锁(DCL)、登记式/静态内部类和枚举方式。每种方式的特点和适用场景都有所不同,其中枚举方式被推崇为最佳实践。
摘要由CSDN通过智能技术生成

单例模式的引发的陈年回忆

  记着N年以前,那时候还在上大学,有一门科目叫做软件体系结构,教我们的老师是个40岁左右的女老师,姓韩,好像是东北大学博士毕业的,之所以对她还有些印象,那是因为初恋女友Y同学是被保送到东北大学读研的,而当时韩老师和Y同学的师生关系好像还挺不错,当然也是Y同学确定保送东北大学读研的那一年,我们和平而坚决地分开了。。

  我就读的大学是一所普通本科,我们学院一个年级可以保送的名额也仅仅只有三个,比起985 211那成堆的保送名额真的差的太远,Y同学是相当优秀的,大学四年,每次期末成绩必然是专业第一,而我,虽然一直在角落里面默默努力着,可成绩总是不尽人意,中游水平,也是因为某次关键的考试少考了一名,错过了成为党员的机会。。。。

  后来我也加入了考研大军,虽然最终成绩比国家线高了50多分,可是距离我的目标院校还少了10分左右,所以我又输了。。。。

  于是在调剂,二战,工作之间,,纠结半天,选择了我最无奈但是方向最明确的一个:工作。毕竟,我父母年纪不小了,是时候赚钱了。。

  我大学时技术不好,或者说大部分人普遍技术都不好,虽然是软件工程专业的,但是实践机会太少,大部分时间都在上那些无聊的理论课,有些理论课,可以无聊到让全班大部分人睡着或者上课玩手机,比如离散数学,计算机组成原理。。 

  而软件体系结构这门课,算是无聊中的理论课相对有点激情的科目,因为,韩老师每次上课前都提问上节课的知识点。。。还会慷慨激昂的挥斥方遒指点江山,鼓励我们考研考研!!这门课就是给我们讲解设计模式的,不过,那些设计模式具体实现我大多忘却了,只记着一些名字:工厂模式,抽象工厂模式,桥接模式,适配器模式,命令模式,建造者模式,观察者模式,迭代器模式,策略模式....有一个很有趣的模式我却还记着比较清楚:单例模式。因为当时分为懒汉式和饿汉式,自己感觉很有趣(那时的快乐就那么简单),而且找工作时也有面试官让我手写单例模式(幸亏写出来了)。。 

  最近随意点开了一篇博客,讲单例模式的,原本没想仔细看,然而却发现,单例模式居然有六种实现模式,而我知道的两种仅仅是最简单的两种,突然感觉自己很无知。。(啪啪打脸)

 于是乎,自己拜读一番,然后写了点demo。。今天也是打算正儿八经聊聊单例模式的。。不过有些触景生情,一不小心扯远了。

  好吧,现在我们言归正传,开始聊单例模式和它的六种实现。

  再稍等下,我想说,当年软件体系结构这门课也是考了89的人,不高但是也不低啊,嘻嘻。

单例模式--从私有化无参构造函数开始

  我不知道该怎么陈述我的话语,以前写博客都是大摆理论知识加点自写的demo代码,坦白讲,连我自己都不想看,于是想从这篇开始做出一些改变,用通俗的话语来讲技术理论讲明白,第一次尝试这种风格,还请观众给点勇气,多多鼓掌。

  所谓单例模式,从字面上来讲,那就是单个实例的模式,只创造一个实例。我们想一下哈,平时工作写代码时,是不是动不动就new一下,动不动就给一个实体创造一个对象,那样的话我们就可以针对某个实体创造多个了实例了,我们都明白,在新建一个实体后,即使不声明构造方法java也是会默认的提供一个无参构造方法的,而我们平时new 实例也就是通过这个无参构造方法实现的,现在将这个无参构造方法声明为私有的,那么就不允许外部去创建了,举个例子。

1

2

3

Zae z1 = new Zae();

Zae z2 = new Zae();

Zae z3 = new Zae();

  

这种未声明私有时,可以创建了3个实例z1,z2,z3哎,但是你一旦在实体中加入:private Zae(){}。 那么你再这样写,保准红色波浪线等着你,编译期就给你亮红灯。

那么我们又有疑问了,单例模式既然是不允许创造多个实例,但是允许创造一个实例啊,你这样不就一个实例也创造不出来么,你该怎么样挡住悠悠之口。
接下来,将是小z给你讲一下实现单例的方式了,在讲之前,先给大家看一段最简单的单例模式实现代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public class SingletonBeanHunger {

 

private SingletonBeanHunger(){}

 

private static SingletonBeanHunger singletonBeanHunger = new SingletonBeanHunger();

 

public static SingletonBeanHunger getInstance(){

    return singletonBeanHunger;

}

 

public void draw(){

    System.out.println("hunger-singleton");

}

}

有没有看到,有没有看到,无参构造函数率先被声明私有的了,而创建的那唯一各一个单例也是被声明私有的了,只是提供了一个公有的调用返回方法。可能你此刻有点蒙,小Z你刚刚说java会默认提供一个无参构造方法,但是你现在声明的这个私有的无参构造方法明明是你自己写的,和系统提供的有什么关系?哈哈,那是因为java的一种默认机制了,当没有定义构造方法时每个类里都有一个默认的无参的构造方法,此时该类就只有一个构造方法;而当你显示的去定义类的构造方法时,那就没有那个默认的构造方法了,该类所有的构造方法就是定义了的那些构造方法。  

有心人发现,为什么要将创建单例的代码变量声明为static静态的方法,这就是高明之处,也是重点了,因为类的加载机制,大家都明白变量里面有静态变量和成员变量的,而静态变量它的大哥是类(Class),类创建它就创建,类销毁它就销毁,而成员变量的大哥是对象,也就是那个所谓的实例,对象创建出来它就创建,对象销毁它也销毁。因此呢,类指定只有一个的,也就是只会被加载一次,那么由此引发的静态变量也就创建出这么独一份了,进而起到了单例的效果。而提供公共的调用方法getInstance(),这个方法时将类加载时创建的那个单例返回出去,因此别人想使用这个单例时,拿到的也就是独一份的了,地址也是相同的。至于那个微不足道的draw()方法,这不是重点,因为它是个成员方法,也就只有对象可以调用了,这个是用来测试我调用getInstance()时拿到的是SingletonBeanHunger这个类的对象而已。

通过上述的讲解大家也差不多的明白单例的概念以及简单实现逻辑,记住这几点:1、单例只能有一个实例。2、单例类必须自己创建自己的唯一实例。3、单例类必须给所有其他对象提供这一实例。其实单例模式吧它主要是解决一个全局使用的类频繁的创建和销毁的,毕竟有时太过频繁创建销毁实例,对JVM也是一个极大的挑战,所以当我们想控制实例的数目和节省系统资源时,不妨考虑下单例模式。他虽然优点不少,但是也有些不可避免的缺点,无参构造都被私有化了,那么当然不能被继承了,也就没有啥接口了,否则就和单一职责原则冲突了。

单例模式和它的六大战将

  其实大家看到这个标题还是有点蒙的,六大战将是什么鬼,你怎么不说是四大天王呢,你咋不上天呢。(汗。。)其实六大战将就是六种实现方式了,因为最近在看部网络水文,其中就讲到欧洲第一杀手“皇帝”和他的八大战将,所以就想到了六大战将了,哈哈哈,我真乃人才也。。(还要不要脸。。)

战将一:伯爵(懒汉式-线程不安全)

这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。这种方式 lazy loading(延迟加载) 很明显,不要求线程安全,在多线程不能正常工作。

 

/**
 * 懒汉式加载-线程不安全
 */
public class SingletonBeanLazyDg {

    private SingletonBeanLazyDg(){}

    private static SingletonBeanLazyDg singletonBeanLazyDg;

    public static  SingletonBeanLazyDg getInstance(){
     if(singletonBeanLazyDg == null){
       singletonBeanLazyDg = new SingletonBeanLazyDg();
     }
    return singletonBeanLazyDg;
 } 

public void draw(){ 
System.out.println("lazy-singleton-dangerous"); 
}
}

 

战将二:耶稣(懒汉式-线程安全)

这种方式具备很好的 lazy loading(延迟加载),能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。

 

/**
 * 懒汉式加载-线程安全
 */
public class SingletonBeanLazy {

    private SingletonBeanLazy (){}

    private static SingletonBeanLazy singletonBeanLazy;

    public static synchronized SingletonBeanLazy getInstance(){
        if(singletonBeanLazy==null){
            singletonBeanLazy = new SingletonBeanLazy();
        }
        return singletonBeanLazy;
    }

    public void draw(){
        System.out.println("lazy-singleton");
    }
}

战将三:野兽(饿汉模式)

这种方式没有加锁,执行效率会提高。但是类加载时就初始化,容易产生垃圾对象,浪费内存。 它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading (延迟加载)的效果。

 

/**
 * 饿汉式
 */
public class SingletonBeanHunger {

    private SingletonBeanHunger(){}

    private static SingletonBeanHunger singletonBeanHunger = new SingletonBeanHunger();

    public static SingletonBeanHunger getInstance(){
        return singletonBeanHunger;
    }

    public void draw(){
        System.out.println("hunger-singleton");
    }
}

 

战将四:鬼影(双检锁/双重校验锁(DCL,即 double-checked locking))

这种方式采用双锁机制,安全且在多线程情况下能保持高性能。

/**
 * 双重校验锁/双检锁
 */
public class SingletonBeanDCL {

    private SingletonBeanDCL(){}

    private static volatile SingletonBeanDCL singletonBeanDCL;

    public static SingletonBeanDCL getInstance(){
        if(singletonBeanDCL==null){
            synchronized (SingletonBeanDCL.class){
                if (singletonBeanDCL == null){
                    singletonBeanDCL = new SingletonBeanDCL();
                }
            }
        }
        return singletonBeanDCL;
    }

    public void draw(){
        System.out.println("DCL-singleton");
    }
}

 

战将五:金童(登记式/静态内部类)

这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。

/**
 * 登记式/静态内部类
 */
public class SingletonBeanStatic {

    private SingletonBeanStatic(){}

    private static class  SingletonBeanHolder{
        private static final SingletonBeanStatic SINGLETON_BEAN_STATIC = new SingletonBeanStatic();
    }

    public static final SingletonBeanStatic getInstance(){
        return SingletonBeanHolder.SINGLETON_BEAN_STATIC;
    }

    public void draw(){
        System.out.println("登记式/静态内部类");
    }
    
}

 

 

战将六:玉女(枚举)

虽然没被广泛应用,但是这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。 这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。

/**
 * 枚举
 */
public enum SingletonBeanEnum {
    INSTANCE;
    public void draw(){
        System.out.println("枚举式单例");
    }
}

 

 

大Boss:皇帝(Main-单例)

我只是一个无辜的测试类

/**
 * 测试单例六种实现
 */
public class TestSingle {
    public static void main(String[] args) {
        //懒汉式-线程不安全
        SingletonBeanLazyDg singletonBeanLazyDg = SingletonBeanLazyDg.getInstance();
        singletonBeanLazyDg.draw();
        
        //饿汉式-线程安全
        SingletonBeanHunger singletonBeanHunger = SingletonBeanHunger.getInstance();
        singletonBeanHunger.draw();

        //懒汉式
        SingletonBeanLazy singletonBeanLazy = SingletonBeanLazy.getInstance();
        singletonBeanLazy.draw();

        //双检锁/双重锁校验
        SingletonBeanDCL singletonBeanDCL = SingletonBeanDCL.getInstance();
        singletonBeanDCL.draw();

        //登记式/静态内部类
        SingletonBeanStatic singletonBeanStatic = SingletonBeanStatic.getInstance();
        singletonBeanStatic.draw();

        //枚举
        SingletonBeanEnum singletonBeanEnum = SingletonBeanEnum.INSTANCE;
        singletonBeanEnum.draw();
    }
}

 

 

### 回答1: 迪兰的RX580战将BIOS是电脑显卡的一个重要组成部分,也是用于管理显卡的系统。BIOS是一种只读存储器芯片,里面包含了显卡的最基本信息,比如无法更改的显卡型号、显存容量、核心频率等。同时,它还包含了显卡的驱动程序、图形处理单元程序等关键信息。如果BIOS出了问题,有可能导致显卡崩溃、无法完全显示图像等问题。 而迪兰的RX580战将BIOS相对于其他的BIOS来说更具优势。它可以针对相应的操作系统进行个性化的调整,使得显卡的性能更好,游戏运行更流畅。此外,战将BIOS还拥有更多的调整选项,可以更加灵活地优化显卡,满足不同用户的需求。 总之,迪兰的RX580战将BIOS可以为用户带来更好的显卡体验。如果需要优化显卡性能,可以根据自己的需求下载BIOS刷入显卡中。但是在操作时需要谨慎,避免出现不必要的错误。最好先备份原始BIOS再进行BIOS更新操作,以免出现不可逆转的情况。 ### 回答2: 迪兰RX580战将 BIOS 是指用于控制AMD Radeon RX 580显卡的BIOS固件程序,以实现对显卡的识别、调整和管理,提高显卡的性能和稳定性。 迪兰RX580战将 BIOS 包含了一系列参数,例如GPU频率、显存大小、显存时序等,可以通过修改这些参数来实现超频、刷BIOS等操作。同时,BIOS中的风扇控制和电源管理等设置也可以影响显卡的工作温度和功耗,进而影响性能和稳定性。 在使用时,需要特别注意BIOS版本和更新历史,谨慎选择BIOS进行升级和修改,以免造成不可逆的硬件损坏或系统不稳定。建议在进行重要操作时备份原始BIOS,以备不时之需。 总之,迪兰RX580战将 BIOS 对于显卡使用者来说是非常重要的系统软件,必须谨慎操作和维护。 ### 回答3: 迪兰RX580战将BIOS是一种可以重写显卡基本输入输出系统(BIOS)的工具,它可以改变显卡的时钟频率,电压和功率限制等参数,以增加显卡的性能。该BIOS使用了先进的CoolTech风扇技术和6相供电系统,提供了更好的散热性能和电源稳定性,以满足高性能游戏和计算需求。 使用迪兰RX580战将BIOS需要一定的技术和经验,如果修改不当可能会导致系统不稳定和显卡故障,因此需要在确保安全的情况下进行操作。此外,修改BIOS也有可能违反了一些厂商的保修条款,用户需要自行承担风险。 总之,迪兰RX580战将BIOS是一种可以增强显卡性能的高级工具,但需要用户拥有一定的技能和知识来使用它。在使用之前应该仔细评估其风险和收益,确保操作正确和安全。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值