多线程 4

1. 单例模式

单例模式是一种设计模式。它的核心思想是一个类在整个应用程序中只创建一个实例,并提供一个统一的访问接口

设计模式 : 应对常见问题的解决方案。假设女朋友经常生气,但每次用 道歉 + 买礼物 就能很快和女朋友和好

"道歉 + 买礼物" 在某种意义上就是设计模式。单例模式就是应对程序反复出现的问题的解决方案

单例模式最常见的 2 种实现方式 : 1. 懒汉模式 2. 饿汉模式

2. 饿汉模式

在类加载的时候,创建实例

java 复制代码
class Singleton{
    private static Singleton instance = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return instance;
    }
}

3. 懒汉模式

在使用的时候创建实例

java 复制代码
class SingletonLazy{
    private static SingletonLazy instance = null;
    public static SingletonLazy getInstance(){
        //把实例的创建时机推迟到 第一次使用的时候
        if(instance == null){
            instance = new SingletonLazy();
        }
        return instance;
    }
    private SingletonLazy(){}
}

多线程懒汉模式

饿汉模式不涉及到修改操作,return instance 是读操作,多线程去读同一个变量,没有线程安全问题

java 的赋值虽然是原子的,前提是符合 instance == null,只有符合条件才能赋值,所以需要把条件判断和赋值操作看作一个整体。在多线程的情况下,如果有 2 个线程同时 getInstance 获取实例,那就有可能在 线程1 条件判断后,操作系统去调度线程2,线程2 创建 实例1 完成后,再次调度回线程1,那么 线程1 就会重复创建实例 ------实例2,虽然 线程2 创建的实例2由于没有被引用,被回收了, 但这仍然是线程安全问题

懒汉模式的线程不安全只是在创建实例之前,创建好实例之后都是读操作,不会存在线程安全问题

解决方式就是把这 2 个操作打包成一个整体,也就是加锁 :

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

这样有一个线程加锁,即使是去调度其他线程其他线程会进入堵塞状态,实例就不会被多次创建。这样线程安全问题就解决了。但是每次调用 getInstance() 都会加锁,不管这个实例是不是创建了。懒汉模式只是在实例创建前存在线程安全问题,这样不必要的加锁,就会导致其他线程堵塞,影响效率

解决方式是在加锁前进行判断是否要加锁 :

java 复制代码
public static SingletonLazy getInstance(){
        //判断是否要加锁
        if(instance == null){
            synchronized (locker){
                if(instance == null){
                    instance = new SingletonLazy();
                }
            }    
        }
        return instance;
    }

如果实例已经创建好了,那就不需要加锁,如果没创建好实例,那就需要加锁保证线程安全,不必要的加锁就不存在了。

但是 instance = new SingletonLazy();可能存在指令重排序的情况的,在创建实例的时候可以分成 3 个步骤:

  1. 分配内存空间
  2. 针对空间进行初始化
  3. 把内存空间的地址赋值给引用变量

但是由于指令重排序,1 => 2 => 3 可能会变成 1 => 3 => 2 很可能后面的代码会使用未初始化的成员属性,对代码逻辑产生影响。那么应该使用 volatile 关键字,禁止指令重排序

java 复制代码
private volatile static SingletonLazy instance = null;
相关推荐
文丑颜不良啊5 分钟前
LeetCode1481~1500题目解析
java·leetcode
2601_962885726 分钟前
quotes 字段全解:涨跌幅、振幅、换手率的口径与单位
java·前端·spring
前端精髓31 分钟前
调用openrouter的免费API模型
java·前端·javascript
步行cgn1 小时前
Spring MVC 拦截器 Interceptor 详解:从执行流程到实战应用
java·spring·mvc
文丑颜不良啊1 小时前
LeetCode1381~1400题目解析
java·leetcode
开开心心_Every1 小时前
快速隐藏程序工具自定义快捷键一键切换
java·开发语言·前端·spring·智能手机·word·intellij-idea
萧瑟余晖2 小时前
Spring Batch核心模型与快速上手详解
java·spring·batch
缓慢更新2 小时前
AI 辅助生成单测:从接口定义到用例的流水线
java·服务器·前端·网络·数据库·人工智能
LMT流氓兔4 小时前
第五单元——第十六课:给书店助手加上网页界面
java·开发语言·jvm·人工智能·oracle·langchain·1024程序员节
wangbing11255 小时前
平台介绍-modbus设备调试
java