常用设计模式系列(六)—单例模式

第一节

前言

        各位大佬好,由于本人的原因,拖更了几天休息了一下,吃了点好吃的,为了填饱自己的肚子,逛遍了天下美食(我所在这个城市的某个角落),所谓干饭人干饭魂,干饭人吃饭得用盆。

        

        吃饱喝足之后,活还是要干的,所以今天继续开始我们的设计模式,今天讲解设计模式之“单例模式”,单例模式算的上是在整个设计模式体系中最为简单的模式,它依然属于创建型分类的对象模式。目前较为流行的Spring,将项目中的对象进行了管理,其bean工厂使用的设计模式就是单例模式,我来详解下单例模式。

第二节

单例模式概念

    

        单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

    表现形式

1

单例类只能有一个实例。

2

单例类必须自己创建自己的唯一实例。

3

单例类必须给所有其它对象提供这一实例。

   个人理解

        单例模式的主要表现为:单例模式创建的类在整个系统中全局唯一,不能够被多次创建,并且只能通过自己创建自己的实例。

场景举例:

        在使用电脑操作excel文件时,如果别的程序已经打开了这个excel文件,系统会提示这个文件就已经被其它程序占用,从而不能够操作这个文件,创建出来的这个文件就是全局唯一的,如果别人想使用,那么需要我操作完毕之后,下一个人才能使用。

   UML类图

第三节

代码实现单例模式

   简单代码实现

        1.创建单例对象

package com.yang.Singleton;
/** * @ClassName Singleton * @Description 单例代码 * @Author IT小白架构师之路 * @Date 2020/12/15 19:09 * @Version 1.0 **/public class Singleton {    //类初始化时创建对象    private static Singleton singleton;
    //把构造方法访问权限改为私有,防止多次创建对象    private Singleton(){
    }    //定义获取对象的静态方法    public static Singleton getSingleton(){        if(null == singleton){            singleton = new Singleton();        }        return singleton;    }}

        2.创建客户端测试

package com.yang.Singleton;
/** * @ClassName Clent * @Description 注释 * @Author IT小白架构师之路 * @Date 2020/12/15 19:14 * @Version 1.0 **/public class Client {    public static void main(String[] args) {        //创建对象        Singleton singleton = Singleton.getSingleton();        //创建对象        Singleton singleton1 = Singleton.getSingleton();        System.out.println(singleton == singleton1);    }}

        3.结果测试

true

        经过测试,发现是单例对象没错,两个对象的地址是一样的。此时我们的程序都是在单个线程下执行的,如果进行多线程测试,创建对象实例会不会是线程安全的呢?写个代码测试下。

   线程安全测试

1.编写Runnbale

package com.yang.Singleton;
import sun.jvm.hotspot.runtime.Thread;
/** * @ClassName SingletonThread * @Description 注释 * @Author IT小白架构师之路 * @Date 2020/12/15 19:55 * @Version 1.0 **/public class SingletonRunble implements Runnable {    @Override    public void run(){        try {            Singleton singleton = Singleton.getSingleton();            java.lang.Thread.sleep(100);            System.out.println(singleton);        }catch (Exception e){            e.printStackTrace();        }
    }}

        2.编写测试类,创建11个线程进行测试

package com.yang.Singleton;
import java.util.HashSet;import java.util.Set;
/** * @ClassName SingletonThreadClient * @Description 注释 * @Author IT小白架构师之路 * @Date 2020/12/15 19:57 * @Version 1.0 **/public class SingletonThreadClient {    public static void main(String[] args) throws Exception{        for (int i = 0; i<=10 ; i++){            Runnable runnable = new SingletonRunble();            Thread thread = new Thread(runnable);            thread.start();        }        Thread.sleep(5000);    }}

        3.测试结果如下:    

对象地址:com.yang.Singleton.Singleton@266a1d19对象地址:com.yang.Singleton.Singleton@266a1d19对象地址:com.yang.Singleton.Singleton@266a1d19对象地址:com.yang.Singleton.Singleton@266a1d19对象地址:com.yang.Singleton.Singleton@266a1d19对象地址:com.yang.Singleton.Singleton@266a1d19对象地址:com.yang.Singleton.Singleton@680cc142对象地址:com.yang.Singleton.Singleton@266a1d19对象地址:com.yang.Singleton.Singleton@298c37fd对象地址:com.yang.Singleton.Singleton@266a1d19对象地址:com.yang.Singleton.Singleton@266a1d19

        测试结论发现,此种创建对象的单例模式,在多线程使用的场景下,出现了线程安全问题,使用的的并非是同一个对象,如何解决呢?我会通过讲解几种单例模式创建方式,来验证每种单例模式创建方式是否会有线程安全问题。

提前了解几个概念:

懒汉式:比较慵懒的方式,只有需要的时候我才去生成对象。

饿汉式:提前生成对象,但是所有人只能用一种对象

饱汉式:此种方式不是单例模式,就是每次需要都生成新的对象,默认的构造就是这种方式。

    单例模式创建的几种方式

        1.非线程安全的懒汉式(上方已经验证过的创建方式)

package com.yang.Singleton;
/** * @ClassName SingletonLh * @Description 懒汉式-非线程安全方式 * @Author IT小白架构师之路 * @Date 2020/12/15 19:33 * @Version 1.0 **/public class SingletonLh {
    //类初始化时创建对象    private static SingletonLh singletonLh;
    //把构造方法访问权限改为私有,防止多次创建对象    private SingletonLh() {    }        //获取对象    public static SingletonLh getInstance() {        if (singletonLh == null) {            singletonLh = new SingletonLh();        }        return singletonLh;    }}

        2.加锁后线程安全的懒汉式

            2.1  代码编写

package com.yang.Singleton;
/** * @ClassName SingletonLhSafe * @Description 懒汉式-线程安全方式 * @Author IT小白架构师之路 * @Date 2020/12/15 17:37 * @Version 1.0 **/public class SingletonLhSafe {    //类初始化时创建对象    private static SingletonLhSafe singletonLhSafe;
    //把构造方法访问权限改为私有,防止多次创建对象    private SingletonLhSafe() {    }
    //获取对象    public synchronized static SingletonLhSafe getInstance() {        if (singletonLhSafe == null) {            singletonLhSafe = new SingletonLhSafe();        }        return singletonLhSafe;    }}

        2.2 测试结果所知,为线程安全的

对象地址:com.yang.Singleton.SingletonLhSafe@6ebc1cfd对象地址:com.yang.Singleton.SingletonLhSafe@6ebc1cfd对象地址:com.yang.Singleton.SingletonLhSafe@6ebc1cfd对象地址:com.yang.Singleton.SingletonLhSafe@6ebc1cfd对象地址:com.yang.Singleton.SingletonLhSafe@6ebc1cfd对象地址:com.yang.Singleton.SingletonLhSafe@6ebc1cfd对象地址:com.yang.Singleton.SingletonLhSafe@6ebc1cfd对象地址:com.yang.Singleton.SingletonLhSafe@6ebc1cfd对象地址:com.yang.Singleton.SingletonLhSafe@6ebc1cfd对象地址:com.yang.Singleton.SingletonLhSafe@6ebc1cfd对象地址:com.yang.Singleton.SingletonLhSafe@6ebc1cfd

3. 饿汉式(线程安全)

    3.1 代码实现

package com.yang.Singleton;
/** * @ClassName SingletonEh * @Description 线程安全的饿汉式 * @Author IT小白架构师之路 * @Date 2020/12/15 17:44 * @Version 1.0 **/public class SingletonEh {    //类初始化时创建对象    private static SingletonEh singletonEh = new SingletonEh();
    //把构造方法访问权限改为私有,防止多次创建对象    private SingletonEh() {    }
    //获取对象    public static SingletonEh getInstance() {        return singletonEh;    }}

        3.2  测试结果,为线程安全

对象地址:com.yang.Singleton.SingletonEh@4952fb3e 对象地址:com.yang.Singleton.SingletonEh@4952fb3e 对象地址:com.yang.Singleton.SingletonEh@4952fb3e 对象地址:com.yang.Singleton.SingletonEh@4952fb3e 对象地址:com.yang.Singleton.SingletonEh@4952fb3e 对象地址:com.yang.Singleton.SingletonEh@4952fb3e 对象地址:com.yang.Singleton.SingletonEh@4952fb3e 对象地址:com.yang.Singleton.SingletonEh@4952fb3e 对象地址:com.yang.Singleton.SingletonEh@4952fb3e对象地址:com.yang.Singleton.SingletonEh@4952fb3e对象地址:com.yang.Singleton.SingletonEh@4952fb3e

        4.提高性能的volatile关键字方式的双检锁/双重锁方式,从编译器级别指定此对象共享。

        4.1  代码编写:

package com.yang.Singleton;
/** * @ClassName SingletonDoubleLocl * @Description 提高性能的双检锁方式 * @Author IT小白架构师之路 * @Date 2020/12/15 20:19 * @Version 1.0 **/public class SingletonDoubleLock {    //类初始化时创建对象,使用volatile关键字    private volatile static  SingletonDoubleLock singletonDoubleLock;
    //把构造方法访问权限改为私有,防止多次创建对象    private SingletonDoubleLock() {    }
    //获取对象    public synchronized static SingletonDoubleLock getInstance() {        if (singletonDoubleLock == null) {            synchronized(Singleton.class){                singletonDoubleLock = new SingletonDoubleLock();            }        }        return singletonDoubleLock;    }}

        4.2  验证结果:为线程安全的

对象地址:com.yang.Singleton.SingletonDoubleLock@266a1d19对象地址:com.yang.Singleton.SingletonDoubleLock@266a1d19对象地址:com.yang.Singleton.SingletonDoubleLock@266a1d19对象地址:com.yang.Singleton.SingletonDoubleLock@266a1d19对象地址:com.yang.Singleton.SingletonDoubleLock@266a1d19对象地址:com.yang.Singleton.SingletonDoubleLock@266a1d19对象地址:com.yang.Singleton.SingletonDoubleLock@266a1d19对象地址:com.yang.Singleton.SingletonDoubleLock@266a1d19对象地址:com.yang.Singleton.SingletonDoubleLock@266a1d19对象地址:com.yang.Singleton.SingletonDoubleLock@266a1d19对象地址:com.yang.Singleton.SingletonDoubleLock@266a1d19

        5.静态内部类的方式(内部类初始化对象)

            5.1 代码编写

package com.yang.Singleton;
/** * @ClassName SingletonInside * @Description 静态内部类的方式 * @Author IT小白架构师之路 * @Date 2020/12/15 20:33 * @Version 1.0 **/public class SingletonInside {    //构造函数私有化    private SingletonInside(){    }    //定义静态内部类    private static class SingletonInsideClass{        private static final SingletonInside SINGLETON_INSIDE  = new SingletonInside();    }    //返回对象方法    public static final SingletonInside getInstance(){        return SingletonInsideClass.SINGLETON_INSIDE;    }}

            5.2 验证结果,静态内部类创建的方式也是线程安全的

对象地址:com.yang.Singleton.SingletonInside@24b0c0a对象地址:com.yang.Singleton.SingletonInside@24b0c0a对象地址:com.yang.Singleton.SingletonInside@24b0c0a对象地址:com.yang.Singleton.SingletonInside@24b0c0a对象地址:com.yang.Singleton.SingletonInside@24b0c0a对象地址:com.yang.Singleton.SingletonInside@24b0c0a对象地址:com.yang.Singleton.SingletonInside@24b0c0a对象地址:com.yang.Singleton.SingletonInside@24b0c0a对象地址:com.yang.Singleton.SingletonInside@24b0c0a对象地址:com.yang.Singleton.SingletonInside@24b0c0a对象地址:com.yang.Singleton.SingletonInside@24b0c0a

        6.枚举方式的创建

            6.1代码测试

package com.yang.Singleton;/** * @ClassName SingletonEh * @Description 枚举的方式创建 * @Author IT小白架构师之路 * @Date 2020/12/15 20:44 * @Version 1.0 **/public enum SingletonEmu {    SINGLETON_EMU;    private SingletonEmu(){    }}

            6.2验证结果,每次都是自己,创建对象过程为线程安全的

对象地址:SINGLETON_EMU对象地址:SINGLETON_EMU对象地址:SINGLETON_EMU对象地址:SINGLETON_EMU对象地址:SINGLETON_EMU对象地址:SINGLETON_EMU对象地址:SINGLETON_EMU对象地址:SINGLETON_EMU对象地址:SINGLETON_EMU对象地址:SINGLETON_EMU对象地址:SINGLETON_EMU

第四节

单例模式优缺点及适用场景

优点:

        1.在单例模式中,所有需要的对象实例,这个实例只有一个,减少了内存的消耗,也可以减少频繁创建销毁实例的过程(Spring框架应用的比较优秀)。

        2.可以减少对资源的多重占用,减少线程安全问题,例如一个写文件操作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作

        3.单例模式可以在系统设置全局的访问点,优化和共享资源访问

缺点:

        1.单例模式没有抽象层,不可以扩展,需要扩展则需要修改源码。

        2.单例模式下,对象的职责会很重

        3.不能应用于线程池、连接池相关场景,因为全局只有一个连接对象。

适用场景:

        1.需要进行内存优化,防止系统频繁创建对象的场景。

        2.进行文件修改,防止其他程序对此程序同时修改造成脏数据场景。

        3.需要进行全局设置的变量访问点,用来共享资源时

        4.线程间相互通信时。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值