多线程单例模式

设计模式,作用,单例模式,作用,实例,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();
    }
}
相关推荐
xiaotianyuanma1 小时前
【计算机毕业设计】基于Spring Boot的体育馆预约系统的设计与实现
java·spring boot·课程设计
何以解忧,唯有..1 小时前
MySQL 索引分类与底层原理深度剖析
java
水无痕simon1 小时前
6、六大工作模式-2
java·rabbitmq
2601_963870212 小时前
【计算机毕业设计】基于Spring Boot的非遗文创产品交易平台的设计与实现
java·spring boot·后端
lv__pf2 小时前
Spring之AOP底层源码解析(下)【TL spring14】
java·后端·spring
水无痕simon3 小时前
5、六大工作模式-1
java
KhalilRuan3 小时前
UnityCsReference——笔记
java·开发语言·笔记
何以解忧,唯有..3 小时前
SpringBoot 中 @Transactional 注解失效的 8 种常见场景与解决方案
java
SL-staff3 小时前
AB测试数据失真根因与变量热替换实战:JVS-Rules函数计算器架构解析
java·运维·微服务·函数式编程·规则引擎·变量管理·营销技术