你就是我的唯一【常用设计模系列(六)—单例模式】

目录

一、前言

二、单例模式的概念

概念

表现形式

个人理解

场景举例

uml图

三、代码实现

创建单例对象

1.创建单例测试

2.创建客户端测试

3.结果测试

验证线程是否安全

1.编写Runnbale

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

3.测试结果如下

测试结论

多种创建方式

验证创建方式

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

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

3. 饿汉式(线程安全)

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

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

6.枚举方式的创建,枚举也是目前比较主流的方式

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

优点

缺点

适用场景


一、前言

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

        吃饱喝足之后,活还是要干的,所以今天继续开始我们的设计模式,今天讲解设计模式之“单例模式”,单例模式算的上是在整个设计模式体系中最为简单的模式,它依然属于创建型分类的对象模式。目前较为流行的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.线程间相互通信时。

觉得对您有用的话,的关注下公众号吧(一起交流技术)

å¾ç

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT小白架构师之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值