首页 > 其他分享 >Spring设计模式——单例模式

Spring设计模式——单例模式

时间:2023-03-09 09:23:20浏览次数:40  
标签:Spring private static 单例 new 线程 设计模式 public

单例模式

  • 单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。
  • 单例模式是创建型模式

饿汉单例模式

  • 饿汉单例模式在类的加载时候就立即初始化,并且创建对象。
  • 它绝对线程安全,在线程还没出现以前就实例化了,不可能存在访问安全问题

优点:没有加任何锁、执行效率比较高,用户体验比懒汉单例模式更好
缺点:类加载的时候就初始化,不管用与不用都占着空间,浪费了内存,有可能“占着茅坑不拉屎”

package org.example.spring.designpattern.singleton;

/**
 * TODO 饿汉单例模式
 *
 * @author ss_419
 * @version 1.0
 * @date 2023/3/9 08:16
 */
public class HungrySingleton {
    //先静态、后动态
    //先属性、后方法
    //先上后下
    /**
     * 全局访问点,线程先没出现以前就实例化,不管用不用都占用空间
     */
    private static final HungrySingleton INSTANCE = new HungrySingleton();

    private HungrySingleton() {
    }

    // 提供一个全局访问点
    public static HungrySingleton getInstance() {
        return INSTANCE;
    }
}

还有另一种写法,利用静态代码块的机制:


package org.example.spring.designpattern.singleton;

/**
 * TODO 饿汉单例模式——静态代码块
 *
 * @author ss_419
 * @version 1.0
 * @date 2023/3/9 08:19
 */
public class HungryStaticSingleton {
    /**
     * 提供一个全局访问点
     */
    private static final HungryStaticSingleton INSTANCE ;
    
    static {
        INSTANCE = new HungryStaticSingleton();
    }

    private HungryStaticSingleton() {
    }
    
    public static HungryStaticSingleton getInstance() {
        return INSTANCE;
    }
}

饿汉单例模式适合用于单例对象比较少的情况

懒汉单例模式

懒汉单例模式,顾名思义它......比较懒,所以它的特点是:被外部类调用的时候内部类才会加载。

package org.example.spring.designpattern.singleton;

/**
 * TODO 懒汉单例模式
 *
 * @author ss_419
 * @version 1.0
 * @date 2023/3/9 08:23
 */
public class LazySimpleSingleton {
    private LazySimpleSingleton() {
    }

    //静态块,公共内存区域
    private static LazySimpleSingleton lazy = null;

    /**
     * 静态方法
     * 实例不存在的时候new一个
     * @return
     */
    public static LazySimpleSingleton getInstance() {
        if (lazy == null) {
            lazy = new LazySimpleSingleton();
        }
        return lazy;
    }


    /**
     * 创建一个线程
     */
    static class testSingletonThread implements Runnable{
        @Override
        public void run() {
            LazySimpleSingleton singleton = LazySimpleSingleton.getInstance();
            System.out.println(Thread.currentThread().getName() + ":" + singleton);
        }
    }

    /**
     * 测试
     * @param args
     */
    public static void main(String[] args) {
        Thread t1 = new Thread(new testSingletonThread());
        Thread t2 = new Thread(new testSingletonThread());
        t1.start();
        t2.start();
        System.out.println("End");
    }
}

运行结果如下:
在这里插入图片描述
以上执行结果有概率出现两种不同结果,存在很大的线程安全隐患;这里教大家一种新技能,用线程模式调试,手动控制线程的执行顺序来跟踪内存的变化。

线程模式调试

先给testSingletonThread打上断点,如下图所示:
在这里插入图片描述
使用鼠标右键单击断点,切换为Thread模式,如下图所示:
在这里插入图片描述
然后给LazySimpleton类打上断点,同样标记为Thread模式,如下图所示:
在这里插入图片描述
转到客户端测试代码,同样也打上断点,同时改为Thread模式,如下图所示:
在这里插入图片描述
开始Debug之后,会看到Debug控制台可以自由切换Thread的运行状态:
在这里插入图片描述
通过不断切换线程,并观测其内存状态,我们发现在线程环境下LazySimpleSingleton被实例化了两次。有时候我们得到的运行结果可能是相同的两个对象,实际上是被后面执行的线程覆盖了,我们看到了一个假象,线程安全隐患依旧存在。

那么,我们如何来优化代码,使得懒汉单例模式在线程环境下安全呢?看下面的代码,给getInstance()加上synchronized关键字,使这个方法变成线程同步方法:
在这里插入图片描述
双重检查锁懒汉单例模式:

package org.example.spring.designpattern.singleton;

/**
 * TODO 双重检查锁懒汉单例模式
 *
 * @author ss_419
 * @version 1.0
 * @date 2023/3/9 08:48
 */
public class LazyDoubleCheckSingleton {
    //静态块,公共内存区域
    private static LazyDoubleCheckSingleton lazy = null;
    private LazyDoubleCheckSingleton() {
        
    }
    public static LazyDoubleCheckSingleton getInstance() {
        if (lazy == null) {
            synchronized (LazyDoubleCheckSingleton.class){
                if (lazy == null) {
                    lazy = new LazyDoubleCheckSingleton();
                    // 1、分配内存给这个对象
                    // 2、初始化对象
                    // 3、设置lazy指向刚分配的内存地址
                }
            }
        }
        return lazy;
    }
}

最终版本,采用静态内部类的方式:

package org.example.spring.designpattern.singleton;

/**
 * TODO 采用静态内部类的懒汉单例模式
 *
 * @author ss_419
 * @version 1.0
 * @date 2023/3/9 08:52
 */
public class LazyInnerClassSingleton {
    // 使用LazyInnerClassGeneral的时候,默认会先初始化内部类
    // 如果没使用,则内部类是不加载的

    private LazyInnerClassSingleton() {
    }
    // 每一个关键字都不是多余的,static是为了使单例的空间共享,保证这个方法不会被重写、重载
    public static final LazyInnerClassSingleton getInstance() {
        // 在返回结果之前,一定会先加载内部类
        return LazyHolder.INSTANCE;
    }
    // 默认不加载
    private static class LazyHolder{
        private static final LazyInnerClassSingleton INSTANCE = new LazyInnerClassSingleton();
    }
}

史上最牛的单例模式实现方式【防止反射破坏掉单例】

package org.example.spring.designpattern.singleton;

/**
 * TODO 自认为史上最强的单例模式
 *
 * @author ss_419
 * @version 1.0
 * @date 2023/3/9 08:58
 */
public class LazyInnerClassSingletonPlus {
    // 使用LazyInnerClassGeneral的时候,默认会先初始化内部类
    // 如果没使用,则内部类是不加载的

    private LazyInnerClassSingletonPlus() {
        if (LazyHolder.INSTANCE != null){
            throw new RuntimeException("不允许创建多个实例");
        }
    }
    // 每一个关键字都不是多余的,static是为了单例的空间共享,保证这个方法不会被重写、重载
    public static final LazyInnerClassSingletonPlus getInstance(){
        return LazyHolder.INSTANCE;
    }

    // 默认不加载
    private static class LazyHolder{
        private static final LazyInnerClassSingletonPlus INSTANCE = new LazyInnerClassSingletonPlus();
    }
}

线程单例实现ThreadLocal
ThreadLocal不能保证其创建的对象是全局唯一的,但是能保证在单个线程中是唯一的,天生是线程安全的:

package org.example.spring.designpattern.singleton;

/**
 * TODO 线程单例实现ThreadLocal
 *
 * @author ss_419
 * @version 1.0
 * @date 2023/3/9 09:05
 */
public class ThreadLocalSingleton {
    private static final ThreadLocal<ThreadLocalSingleton> threadLocalSingleton = new ThreadLocal<ThreadLocalSingleton>(){
        @Override
        protected ThreadLocalSingleton initialValue() {
            return new ThreadLocalSingleton();
        }
    };
    
    private ThreadLocalSingleton() {
        
    }
    public static ThreadLocalSingleton getInstance() {
        return threadLocalSingleton.get();
    }
}

总结

单例模式可以保证内存里只有一个实例,减少了内存的开销,还可以避免对资源的多重占用。

标签:Spring,private,static,单例,new,线程,设计模式,public
From: https://www.cnblogs.com/atwood-pan/p/17197074.html

相关文章

  • 狂神说SpringBoot学习笔记_暂存
    1.全面接管SpringMVC的配置!实操!2.yaml可以直接给实体类赋值。jar:webapp!自动装配Springboot到底帮我们配置了什么?我们能不能进行修改?能修改哪些东西?能不能扩展?xxxxAut......
  • SSM SpringBoot vue药店管理系统
    SSMSpringBootvue药店管理系统登录首页普通管理员管理用户信息管理会员信息管理生产厂商管理类别信息管理药品信息管理进货管理销售信息管理联系我们管理系......
  • LabVIEW|知识点:设计模式
    1、简单设计模式设计:需求-----方案架构1)简单VI模式:通常无需用户执行指定启示或停止工作。2、状态机1)表示状态的量(枚举/字符串)2)条件结构3)移位寄存器标准设计:1、使用......
  • 设计模式(十七)----行为型模式之模板方法模式
    行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为......
  • python单例模式处理多线程问题
    #单例模式处理多线程的问题importthreadingimporttimeclassSingle:instance=Nonedef__init__(self,name):self.name=namedef__new__(cls,......
  • 关于Android事件分发的设计模式理解与思考
    关于Android事件分发的设计模式理解与思考在现在Android智能机上,触碰几乎成为了唯一的交互方式。那么触碰消息在Android系统当中怎么进行分发的呢?在事件分发处理上,Androi......
  • java springboot mybatis plus 3.4 实现执行任意 sql 语句
    试了SqlRunner一直失败,不知道原因,于是试了如下方法,完美解决。@AutowiredprivateSqlSessionFactorysqlSessionFactory;publicList<Map<String,Object>>exec......
  • SpringBoot上传文件夹的三种解决方案
    ​ 一、概述 所谓断点续传,其实只是指下载,也就是要从文件已经下载的地方开始继续下载。在以前版本的HTTP协议是不支持断点的,HTTP/1.1开始就支持了。一般断点下载时才用......
  • 【配置中心】springboot2.x 整合 zuul
    背景本来是一件很简单的事情,就是想在配置中心添加一层认证,但还是搞得很麻烦,麻烦到要专门做一篇小记来记录一下这坎坷的过程。是这样子的,原来的项目:springboot1.x+zuul......
  • JAVA (Spring Boot)数据AES加密解密
    packagecom.example.controller;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.client.RestTemplate;importjavax......