设计模式-单例模式

Java开发有一句老话,你想学好编码,一定要学习设计模式,本人也是开发上一枚菜鸟,也在努力的补充开发的盲区。。。
学习设计模式,要结合自己的业务需求去使用,或者自己搭建框架,可以使你的代码方便维护,高扩展性,减少代码的冗余等,但是如果你连基础的设计模式怎么写都不知道,那何谈去做框架,去做架构的设计模式开发呢,这里主要是针对设计模式中的基础知识点和基础使用方法给出自己的理解和代码实现。

1.单例模式简介

1.1何为单例设计模式

保证在一个jvm中只存在一个实例,保证对象的唯一性,作为好男人,要学会专一啊,只忠于一个对象

1.2单例模式应用场景。

在我们使用电脑的时候,当你打开任务管理器的时候,将其最小化之后,再次点击任务管理器,还是打开那个被缩小化的任务管理器,这就是单例模式的体现。
再比如java开发框架中的servlet、springMvc、连接池、线程池、枚举都是单例模式,spring中也是默认为单例模式。

1.3为什么使用单例模式?单例的好处?

拿线程池来理解,线程池的最大的好处就是可以重复利用,保存在线程池中的线程,在需要使用的时候,不需要重新创建新的线程,可以很大程度上节约内存,重复利用,方便管理,而单例的最大的缺点就是线程安全问题。在实际的开发过程中,一定要注意单例模式的线程安全问题。

2.单例模式的创建方式

在说单例模式创建之前,先看下以前创建对象的时候,我们使用如下图所示方法:
在这里插入图片描述
这里可以看出,构造方法为public时,使用new创建对象,此时两个对象不是同一个对象。
所以,单例模式创建需要注意的几个点:
(1)将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象;
(2)在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型;
(3)定义一个public static 修饰的静态方法用于返回这个唯一对象。

单例创建方式有以下几种:
1.饿汉式:类初始化时,会立即加载该对象,线程天生安全,调用效率高;
2.懒汉式:类初始化时,不会立即加载该对象,只有真正需要的时候才会创建该对象,具备懒加载功能;
3.双重检测锁:解决懒汉式加锁后造成效率低的问题;
4.枚举方式:枚举天生是单例模式,实现简单,调用效率高,天生安全。

下面从代码的角度来看看具体实现:

2.1饿汉式

饿汉式,就像是一个饥渴的饿汉一样,只要遇到一个“对象”就疯狂扑上去,迅速的带到了“床上”,此处省略1w字,再经历了一番翻云覆雨之后,饿汉也有“吃饱”的时候,也就是不再需要这个“对象”了,但这个“对象”也不是吃醋的,心想:老娘也不是好惹的,向ba啥无情,不存在的,我就赖在你的床上!直到“对象”被香消玉损,孤独终生,才给饿汉让出床位。
在这个例子中,“对象”就是类的对象,“床”就是内存空间,当类的对象被创建,就会在内存中分配一个空间,即使你不再使用这个对象,也一样占用着内存,直到对象被销毁,才会让出内存空间。
在这里插入图片描述
问题一:为什么饿汉式是线程安全的?
从代码中可以看出,首先是将构造函数私有化,保证不会使用new创建多个对象,其次使用public static final 修饰使用new创建的instance对象,由于final关键字的不可修改行,保证了这个对象创建出来是唯一的,所以,不会出现线程安全问题。

2.2懒汉式

懒汉式最大的特点就是,他比较懒,即使遇到对的人,也不会去主动追求,但每年过年回家,家里又会问你有没有“对象”啊之类的话,这个时候,为了避免长辈们的唠叨,即使再懒的人,也需要去找个“对象”充充场面。这就是懒汉式的通俗解释,只有在真正需要用到对象的时候采取实例化对象。
懒汉式有一个最大的特点就是延迟加载,只有在调用get()方法时实例才被创建(先不急着实例化出对象,等要用的时候才给你创建出来。不着急,故又称为“懒汉模式”),常见的实现方法就是在get方法中进行new实例化。

2.2.1基本的懒汉式

在这里插入图片描述
问题二:懒汉模式存在线程安全问题,请解释下?
首先,看下上图中if()语句中的代码,当instance不存在时,采取new一个实例,但加入此时有多个线程访问时,第一个线程判断instance为空,创建实例,但第二个线程如果在线程一未创建完实例时也进入if()中进行判断,就会创建第二个实例,这两个实例是不同的,会造成线程安全问题。
那如何解决这个问题呢?请往下看。

2.2.2使用synchronized关键字修饰getInstance()方法

在这里插入图片描述
分析:在getInstance ()方法上加锁,这样可以保证在同一时刻,只允许有一个线程使用这个方法,创建实例,但是,正式因为同一时刻只有一个线程运行,这种方式又会存在运行效率低的问题。
那如何解决这个问题呢?请往下看。

2.2.3DCL双检查锁机制

DCL双检查锁机制:double checked locking,直接看代码实现。

在这里插入图片描述
分析:第一次检查instance是否被实例化出来,如果没有进入if块,只有当执行instance = new singleton03()这个代码出现问题的时候采取上锁,此时某个线程取得了类锁,实例化对象前第二次检查instance是否已经被实例化出来,如果没有,才最终实例出对象

2.3 枚举创建单例

枚举天生就是单例的,这让我想起了《我是特种兵》里面的一句话:“伞兵天生就是被包围的”,哈哈。。。下面直接看代码。

2.3.1枚举示例

在这里插入图片描述
分析:首先创建一个枚举类,模拟http状态码200和500,在获取httpCode和httpMsg时都是同一个对象
但是,比如枚举类中有两个枚举类型200和500,我们在main()方法中输出时,虽然是创建单例对象,但是构造方法是被运行了两次的!看下图代码。
在这里插入图片描述
分析:在构造方法加入输出信息,发现控制台输出两个构造方法初始化,所以有一个结论,构造方法的调用次数与枚举个数有关

2.3.2枚举实现单例

通过以上的代码,我们了解到枚举天生就是单例这样一个概念,那下面,我们来看下使用枚举如何创建单例,直接看以下代码。
在这里插入图片描述

3.示例代码

3.1预先知识
package com.wshy.testdemo.singletonDemo;

/**
 * @author wshy
 * @data 2020/7/9
 **/
public class singletonPre {

    //无参构造方法
    public singletonPre(){}

    public static void main (String[] args) {

        //使用new调用无参构造方法创建对象
        singletonPre singletonpre1 = new singletonPre ();
        singletonPre singletonpre2 = new singletonPre ();

        System.out.println (singletonpre1 == singletonpre2);

    }
}

3.2饿汉式
package com.wshy.testdemo.singletonDemo;

/**
 * @author wshy
 * @data 2020/7/9
 **/

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

    // 将自身实例化对象设置为一个属性,并用static、final修饰
    private static final singleton01 instance = new singleton01 ();

    //1.构造方法私有化
    private singleton01 () {
    }

    // 静态方法返回该实例
    public static singleton01 getInstance () {
        return instance;
    }

    public static void main (String[] args) {
        singleton01 instance1 = singleton01.getInstance ();
        singleton01 instance2 = singleton01.getInstance ();

        System.out.println (instance1 == instance2);
    }
}

3.3懒汉式
package com.wshy.testdemo.singletonDemo;

/**
 * @author wshy
 * @data 2020/7/9
 **/
public class singleton02 {

    private static singleton02 instance;

    //1.构造方法私有化
    private singleton02 () {}

    // 静态方法返回该实例
    public static synchronized singleton02 getInstance () {
        if(instance == null) {
            instance = new singleton02();
        }
        return instance;
    }

    public static void main (String[] args) {
        singleton02 instance1 = singleton02.getInstance ();
        singleton02 instance2 = singleton02.getInstance ();

        System.out.println (instance1 == instance2);
    }

}

3.4双重检测锁
package com.wshy.testdemo.singletonDemo;

/**
 * @author wshy
 * @data 2020/7/9
 **/
public class singleton03 {
    private static singleton03 instance;
    //1.构造方法私有化
    private singleton03 () {}

    // 静态方法返回该实例
    public static singleton03 getInstance() {
        // 第一次检查instance是否被实例化出来,如果没有进入if块
        if(instance == null) {
            synchronized (singleton03.class) {
                // 某个线程取得了类锁,实例化对象前第二次检查instance是否已经被实例化出来,如果没有,才最终实例出对象
                if (instance == null) {
                    instance = new singleton03();
                }
            }
        }
        return instance;
    }

    public static void main (String[] args) {
        singleton03 instance1 = singleton03.getInstance ();
        singleton03 instance2 = singleton03.getInstance ();

        System.out.println (instance1 == instance2);
    }
}

3.5枚举方式
3.5.1枚举示例
package com.wshy.testdemo.singletonDemo;

/**
 * @author wshy
 * @data 2020/7/9
 **/

/**
 * 模拟http请求状态码和状态信息
 */
public enum singletonEnum {
    /**
     * 请求状态200
     */
    HTTP_200(200,"请求成功!"),
    /**
     * 请求状态500
     */
    HTTP_500(500,"请求失败!");


    singletonEnum (int httpCode, String httpMsg) {
        System.out.println ("构造方法初始化");
        this.httpCode = httpCode;
        this.httpMsg = httpMsg;
    }

    private int httpCode;

    private  String httpMsg;

    public int getHttpCode () {
        return httpCode;
    }

    public void setHttpCode (int httpCode) {
        this.httpCode = httpCode;
    }

    public String getHttpMsg () {
        return httpMsg;
    }

    public void setHttpMsg (String httpMsg) {
        this.httpMsg = httpMsg;
    }

    public static void main (String[] args) {
        System.out.println ("httpCode = " + singletonEnum.HTTP_200.getHttpCode ()  + ",httpMsg=" + singletonEnum.HTTP_200.getHttpMsg ());
    }
}

3.5.2 枚举单例
package com.wshy.testdemo.singletonDemo;

/**
 * @author wshy
 * @data 2020/7/10
 **/
public class singleton04 {
    //私有化构造方法
    private singleton04 () {}

    // 静态方法返回该实例
    public static singleton04 getInstance () {
        return singletol04Enum.INSTANCE.getInstance ();
    }

    //定义一个枚举类,因为枚举天生就是单例的
    static enum singletol04Enum{
        /*
        实例枚举
         */
        INSTANCE;
        private singleton04 singleton04;

        private singletol04Enum(){
            singleton04 = new singleton04 ();
        }

        public singleton04 getInstance () {
            return this.singleton04;
        }
    }

    public static void main (String[] args) {
        singleton04 instance1 = singleton04.getInstance ();
        singleton04 instance2 = singleton04.getInstance ();
        System.out.println (instance1 == instance2);
    }
}

码字不易,请老铁觉得有用留下个小👍,谢谢!

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值