多线程单例模式

设计模式,作用,单例模式,作用,实例,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();
    }
}
相关推荐
phltxy3 小时前
C语言操作符详解
java·c语言·算法
步行cgn4 小时前
@Configuration 详解:Spring 配置类的核心注解
java·后端·spring
sunshine22 girl4 小时前
Java学习一 环境配置2 安装和基本使用Idea
java·学习·intellij-idea
嘿嘿-665 小时前
Windows 一键使用 GPT-6 Astra:Codex CLI 配置教程
java·人工智能·windows·gpt·chatgpt·web
策案案案6 小时前
YOLO: 将AI Agents嵌入到IntelliJ IDEA
java·大数据·人工智能·笔记·yolo·microsoft·intellij-idea
辰烨chenye8 小时前
LeetCode Hot 100 题解 · 堆篇
java·算法·leetcode
shehuiyuelaiyuehao8 小时前
算法29,前缀和,除自身以外的数组的乘积
java·数据结构·算法
小刘在重生~9 小时前
Java常用类|String类详解 + BigDecimal精准计算(含面试题)
java·开发语言·python
随遇而安zx9 小时前
【多线程】---JMM 与 volatile 深度解析
java·多线程