多线程 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;
相关推荐
Rain的Java大神之路1 小时前
[特殊字符]8年Java老兵转型AI Agent:90%的人挂在同一个坑,根本不用学Python!(万字实战复盘,建议收藏)
java·数据库·人工智能·后端·python·spring·ai编程
高级程序源2 小时前
springboot大学生体测系统72170-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·python·django·课程设计
IT研究室2 小时前
最新计算机毕业设计选题推荐-基于spring boot的文学名著分享系统-网站-文档指导-Java-springboot
java·spring boot·课程设计
denlaku2 小时前
JDK 13 新特性详解
java·开发语言
秋饼2 小时前
Java 后端企业级 LLM 网关实战:Spring Cloud Gateway 构建统一大模型接入层
java·ai·技术分享·后端开发
今年下半年2 小时前
【防重放】Spring Boot 通过自定义注解实现接口防重放
java·spring boot·自定义注解·防重放
WeiXin_DZbishe2 小时前
基于Node.js的马拉松赛事管理系统的设计与实现-计算机毕设 附源码【课程设计】90211
java·javascript·vue.js·python·php·课程设计
Wang's Blog2 小时前
Java 项目部署之 Docker工具快速入门: 使用 Docker 镜像命令练习
java·docker
企业数字化笔记2 小时前
多租户系统从哪里开始设计?租户身份、数据隔离、权限与套餐的整体方案
java·spring boot·后端·junit