多线程单例模式

设计模式,作用,单例模式,作用,实例,new多个实例会报错

,饿汉模式1.类成员2.构造方法私有;懒汉模式,区别,比较,线程不安全,

时间消耗,问题出现场景,加锁,加锁后的问题,阻塞->效率,解决;

指令重排序(编译器优化),3步 ,解决,volatile(面试题)

1.设计模式

设计模式:开发中有多个重复的问题场景,把这些步骤总结为固定的解决方案,应用在开发中。这些解决方案就是设计模式。设计模式可以提升开发效率。

设计模式包含单例模式。

2.单例模式

单例模式:一个类在当前进程中只能存在一个实例。多个进程之间无法保证只有一个实例。因为进程之间有独立的存储空间,互不影响,各个进程维护自己的单例对象。

为什么要用到单例模式?

比如:服务器要加载100G的数据到内存中。

在代码中有一个类A的构造方法中进行上述的数据加载(读硬盘文件,把数据加载到内存中),那么在一个对象创建好,就会消耗100G内存空间。这当然是不可取的!

所以在应用中有些类只用创建一次就足够了,只用new一次即可。但是呢,人为因素不可控,所以有了单例模式以确保一个进程中某个类中只有一个实例。

3.饿汉模式

在类加载的时候,在方法区生成类对象,执行初始化静态变量,静态代码块。

类加载:JVM把.class文件加载到JVM内存上,解析这个类

1.触发类加载的时机---只要JVM第一次遇到这个类,就触发类加载

1.程序运行中,第一次访问这个类的静态成员

2.new这个类的对象

3.反射加载这个类

4.懒汉模式

在类加载时部创建对象,在第一次调用getInstance时才new对象。通常认为懒汉模式优于饿汉模式,因为如果代码逻辑中不需要创建instance,那么就省下了创建instance所用的内存空间。

但是,懒汉模式会出现线程安全问题。

5.懒汉模式下的线程安全问题

1.原子性

如图:

在t1执行完后,最终进程里还是只有一个instance,但是,如果new一个对象需要花费10min,又或者同一个程序里保存了两份单例对象,s2修改数据,s1看不到更新的数据,造成业务逻辑错乱。

解决方法:将if与new操作打包为原子的:

复制代码
public static SingletonLazy getInstance(){
   
            synchronized (locker) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        
        return instance;
    }

2.加锁阻塞导致的效率问题

上述1问题只出现在实例化对象时,一旦实例化完成后,后续操作只是读instance。如果每次调用getInstance方法都阻塞,就会导致效率低下。

所以优化为:

复制代码
public static SingletonLazy getInstance(){
        if(instance==null) {
            synchronized (locker) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }

3.指令重排序引起的问题

复制代码
             instance = new SingletonLazy();

这行代码涉及到很多指令,将其分为三个步骤:

1.在堆内存上开辟一块内存空间

2.初始化

3.将堆内存的首地址赋值给引用变量

而这三步中涉及很多指令。

编译器优化,使指令重排序,1->2->3可能变为1->3->2,在多线程下就会出现问题:

注意在分配堆内存空间,成员变量此时都是默认初始值,如int型为0。在分配内存空间后,instance!=null了。

所以用volatile关闭编译器优化

复制代码
package thread;
class SingletonLazy{
    private volatile static SingletonLazy instance=null;
    private static Object locker=new Object();
    

    public static SingletonLazy getInstance(){
        if(instance==null) {
            synchronized (locker) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
    private SingletonLazy(){

    }
}
public class demo22_singletonLazy {
    public static void main(String[] args) {
//        SingletonLazy s1=SingletonLazy.getInstance();
//        SingletonLazy s2=SingletonLazy.getInstance();
        //System.out.println(s1==s2);
        Thread t1=new Thread(()->{
                SingletonLazy s1=SingletonLazy.getInstance();
        });
        Thread t2=new Thread(()->{
            SingletonLazy s2=SingletonLazy.getInstance();
        });
        t1.start();
        t2.start();
    }
}
相关推荐
企业数字化笔记1 分钟前
多租户配额怎么控制?用量口径、并发预留与超额处理的实现方法
java·数据库·spring boot
巴勒个啦29 分钟前
Prompt Engineering 实战:5 个让 AI 生成生产级业务代码的工程模式
java·前端
倚楼听风语1 小时前
Kotlin 协程与 suspend 原理详解
java·算法·kotlin
开开心心就好1 小时前
文件被占用删不掉?解锁工具强制删除
java·运维·服务器·开发语言·前端·pdf·ocr
weixin_460443561 小时前
Java+Linux私有化训考平台:服务、数据库与终端部署结构解析
java·linux·数据库·私有化部署·企业培训考试系统·企业考试系统
云和数据.ChenGuang1 小时前
python大小整数范围解析
java·开发语言·python
Java内核笔记2 小时前
Spring AI 生产级 Chat 接口:6 个开关、13 个坑,其中 2 个开关根本没生效
java·人工智能·后端
雷一言说2 小时前
Spring AI 2.0 RAG:用 Metadata Filter 做多租户检索隔离
java·spring
WeiXin_DZbishe2 小时前
基于springboot装修风格选择系统的设计与开发-计算机毕设【课程设计】77964
java·vue.js·spring boot·后端·python·php·课程设计
XZ-0700012 小时前
week9-三元表达式
java·前端·python